# -*- coding: utf-8 -*-
from maya import cmds
from . import common
from . import lang
from . import qt
import maya.api.OpenMaya as om
import traceback
import json
import os
import imp
import copy
import math
try:
    imp.find_module('PySide2')
    from PySide2.QtWidgets import *
    from PySide2.QtGui import *
    from PySide2.QtCore import *
except ImportError:
    from PySide.QtGui import *
    from PySide.QtCore import *
    
save_path = os.path.join(
        os.getenv('MAYA_APP_DIR'),
        'Scripting_Files')
        
maya_ver = int(cmds.about(v=True)[:4])

class AppendPolygon(qt.SubWindow):
    num_list = None
    pre_mesh = None
    sub_edges = None
    last_edge = None
    mesh = None
    check_normal_flag = False
    undo_flag = False
    save_file = save_path+'\\sisidebar_append_polygon_setting_'+str(maya_ver)+'.json'
    def __init__(self, parent = None, menu_text=95, string_col=255, 
                        mid_color =160, bg_col=52,  ui_color=128, text_col=0, 
                        hilite=192, radio_base_col=255):
        super(AppendPolygon, self).__init__(parent)
        
        self.text_col = text_col
        self.hilite = hilite
        self.selected_list = []#今までの選択状態を記録する
        
        self.load_data()
        
        wrapper = QWidget()
        self.setCentralWidget(wrapper)
        self.main_layout = QVBoxLayout()
        wrapper.setLayout(self.main_layout)
        qt.change_widget_color(self, textColor=menu_text, bgColor=ui_color)
        
        #ソフトエッジ角度の設定
        msg = lang.Lang(
        en='- Smoothing Angle -',
        ja=u'- スムース角の設定 -').output()
        label = QLabel(msg,self)
        qt.change_button_color(label, textColor=menu_text ,  bgColor= ui_color )
        self.main_layout.addWidget(label)
        self.slider_layout = QHBoxLayout()
        self.main_layout.addLayout(self.slider_layout)
        self.soft_angle = QDoubleSpinBox(self)#スピンボックス
        self.soft_angle.setRange(0, 180)
        self.soft_angle.setValue(self.soft_angle_val)#値を設定
        self.slider_layout.addWidget(self.soft_angle)
        qt.change_widget_color(self.soft_angle, textColor=string_col, bgColor=mid_color, baseColor=bg_col)
        #スライダバーを設定
        self.soft_angle_sld = QSlider(Qt.Horizontal,self)
        self.soft_angle_sld.setRange(0, 18000)
        self.soft_angle_sld.setValue(self.soft_angle.value()*100)
        self.slider_layout.addWidget(self.soft_angle_sld)
        #スライダーとボックスの値をコネクト。連動するように設定。
        self.soft_angle_sld.valueChanged.connect(lambda : self.soft_angle.setValue(self.soft_angle_sld.value()/100.0))
        self.soft_angle.editingFinished.connect(lambda : self.soft_angle_sld.setValue(self.soft_angle.value()*100))
        
        self.main_layout.addWidget(qt.make_h_line())
        
        msg = lang.Lang(en='- Component acquisition setting -', ja=u'- コンポーネント取得設定 -').output()
        label = QLabel(msg,self)#スピンボックス
        qt.change_button_color(label, textColor=menu_text ,  bgColor= ui_color )
        self.main_layout.addWidget(label)
        #ラジオボタン作成
        msg = lang.Lang(en='Automatic', ja=u'自動取得').output()
        real_time = QRadioButton(msg, self)
        qt.change_widget_color(real_time, textColor=menu_text,  bgColor=ui_color, baseColor=radio_base_col, windowText=menu_text)
        msg = lang.Lang(en='ボタン入力', ja=u'ボタン入力').output()
        btn_input = QRadioButton(msg, self)
        qt.change_widget_color(btn_input, textColor=menu_text,  bgColor=ui_color, baseColor=radio_base_col, windowText=menu_text)
        #ラジオボタンが縦並びになるよう配置
        self.main_layout.addWidget(real_time)
        self.main_layout.addWidget(btn_input)
        #ラジオボタンを内部的に1まとめにする
        self.input_mode = QButtonGroup(self)
        self.input_mode.addButton(real_time, 0)
        self.input_mode.addButton(btn_input, 1)
        self.input_mode.buttonClicked.connect(self.change_input_mode)
        self.input_mode.buttonClicked.connect(self.save_data)
        self.input_mode.button(self.input_mode_val).setChecked(True)#初期値設定
        
        msg = lang.Lang(en='Get component', ja=u'コンポーネントを取得').output()
        #self.main_layout.addWidget(qt.make_h_line())
        self.comp_but = qt.make_flat_button(name = msg, text=text_col, bg=hilite, ui_color=ui_color, checkable=False, h_max=24, h_min=24)
        self.comp_but.clicked.connect(qt.Callback(lambda : self.append_polygon(button_input=True)))
        self.main_layout.addWidget(self.comp_but)
        
        self.main_layout.addWidget(qt.make_h_line())
        
        msg = lang.Lang(en='- Timing of face fix -', ja=u'- フェース確定のタイミング -').output()
        label = QLabel(msg,self)#スピンボックス
        qt.change_button_color(label, textColor=menu_text ,  bgColor= ui_color )
        self.main_layout.addWidget(label)
        #ラジオボタン作成
        msg = lang.Lang(en='Always fixed', ja=u'常に確定').output()
        always_fix = QRadioButton(msg, self)
        qt.change_widget_color(always_fix, textColor=menu_text,  bgColor=ui_color, baseColor=radio_base_col, windowText=menu_text)
        msg = lang.Lang(en='ボタン入力', ja=u'ボタン入力').output()
        btn_fix = QRadioButton(msg, self)
        qt.change_widget_color(btn_fix, textColor=menu_text,  bgColor=ui_color, baseColor=radio_base_col, windowText=menu_text)
        #ラジオボタンが縦並びになるよう配置
        self.main_layout.addWidget(always_fix)
        self.main_layout.addWidget(btn_fix)
        #ラジオボタンを内部的に1まとめにする
        self.fix_mode = QButtonGroup(self)
        self.fix_mode.addButton(always_fix, 0)
        self.fix_mode.addButton(btn_fix, 1)
        self.fix_mode.buttonClicked.connect(self.change_fix_mode)
        self.fix_mode.buttonClicked.connect(self.save_data)
        self.fix_mode.button(self.fix_mode_val).setChecked(True)#初期値設定
        
        msg = lang.Lang(en='Fix face', ja=u'フェースを確定').output()
        self.fix_but = qt.make_flat_button(name = msg, text=text_col, bg=hilite, ui_color=ui_color, checkable=False, h_max=24, h_min=24)
        self.fix_but.clicked.connect(self.reset_var)
        self.main_layout.addWidget(self.fix_but)
        
        self.create_job()
        self.create_undo_job()
        self.show()
        
        self.change_fix_mode()
        self.change_input_mode()
        
    def load_data(self):
        if os.path.exists(self.save_file):#保存ファイルが存在したら
            with open(self.save_file, 'r') as f:
                try:
                    save_data = json.load(f)
                    self.input_mode_val = save_data['input']
                    self.fix_mode_val = save_data['fix']
                    self.soft_angle_val= save_data['soft_angle']
                except Exception as e:
                    self.input_mode_val = 1
                    self.fix_mode_val = 1
                    self.soft_angle_val = 120
                    print e.message   
        else:
            self.input_mode_val = 1
            self.fix_mode_val = 1
            self.soft_angle_val= 120
            
    def save_data(self):
        save_data = {'input':self.input_mode.checkedId(),
                    'fix':self.fix_mode.checkedId(),
                    'soft_angle':self.soft_angle.value()}
        with open(self.save_file, 'w') as f:
            json.dump(save_data, f)
    
    def change_input_mode(self):
        if self.input_mode.checkedId() == 0:
            self.comp_but.setDisabled(True)
            qt.change_button_color(self.comp_but, textColor=128, bgColor=self.hilite, hover=True, mode = 'button')
        else:
            self.comp_but.setDisabled(False)
            qt.change_button_color(self.comp_but, textColor=self.text_col, bgColor=self.hilite, hover=True, mode = 'button')
        
    def change_fix_mode(self):
        if self.fix_mode.checkedId() == 0:
            self.fix_but.setDisabled(True)
            qt.change_button_color(self.fix_but, textColor=128, bgColor=self.hilite, hover=True, mode = 'button')
        else:
            self.fix_but.setDisabled(False)
            qt.change_button_color(self.fix_but, textColor=self.text_col, bgColor=self.hilite, hover=True, mode = 'button')
            
    undo_job = None
    def create_undo_job(self):
        self.undo_job = cmds.scriptJob(cu=True, e=("Undo", self.undo_control))
        
    def remove_undo_job(self):
        if self.undo_job:
            cmds.scriptJob(k=self.undo_job)
            self.undo_job = None
        
    def create_job(self):
        cmds.selectMode(co=True)
        #cmds.select(cl=True)
        self.reset_var()
        self.script_job = cmds.scriptJob(cu=True, e=("SelectionChanged", qt.Callback(self.append_polygon)))
        self.undo_flag = False
        print 'create append job :', self.script_job
        
    script_job = None
    def remove_job(self):
        if self.script_job:
            print 'remove append job :', self.script_job
            cmds.scriptJob(k=self.script_job)
            self.script_job = None
        
    def closeEvent(self, e):
        self.remove_job()
        self.remove_undo_job()
        self.save_data()
        
    def undo_control(self):
        #print '*-*-*-*-*-*undo control*-*-*-*-*-**-*'
        self.reset_var()
        
    #スクリプトジョブで選択頂点に対してアペンド制御
    def append_polygon(self, button_input=False):
        #print '******selection_change*******'
        #ボタン入力モードかどうかを判定
        if self.input_mode.checkedId() == 1 and not button_input:
            #print 'return in button mode :'
            return
        #print '-----------------------------------'
        if self.check_normal_flag:
            #'return in check mode'
            return
        if self.undo_flag:
            #print '**+*+*+*+*+*+*+*return in undo'
            self.undo_flag = False
            return
        self.sel = cmds.ls(sl=True, fl=True)
        self.mesh = cmds.ls(hl=True, l=True)
        if len(self.mesh) >1 or not self.mesh:
            self.reset_var()
            return
        if self.mesh != self.pre_mesh:
            self.reset_var()
        self.vtx = cmds.filterExpand(self.sel, sm=31)
        self.edge = cmds.filterExpand(self.sel, sm=32)
        if self.edge:
            self.vtx = common.conv_comp(self.edge, mode='vtx')
            self.uv_edge = self.edge[:]
        else:
            self.uv_edge = common.conv_comp(self.vtx, mode='edge')
            self.edge = common.conv_comp(self.vtx, mode='edge')
        if self.vtx:
            self.num_list = self.vtx2num(self.vtx)
            self.num_list = sorted(set(self.num_list), key=self.num_list.index)
        #print 'num_list :',self.num_list
        if self.num_list in self.selected_list:#以前の選択状態に含まれていたら抜ける
            #for nl in self.selected_list:
                #print 'check :', nl
            #print 'return in same selection :', self.num_list
            self.selected_list.remove(self.num_list)
            return
        if len(self.num_list) > 2:#以前の選択状態リストに追加してアンドゥ時の挙動を制御
            self.selected_list.append(self.num_list)
        self.apply_append_poly()
            
            
    #アペンド実行関数
    def apply_append_poly(self):
        #print 'try to check last edge:',self.sub_edges
        if self.sub_edges:
            self.check_last_edge()
        if len(self.num_list) < 3:
            return
        #self.all_edges = common.conv_comp(self.mesh, mode='edge')
        try:
            #print '*+*+**+**+*+*+*append polyton to :', self.num_list[:3]
            num_list = self.num_list[:3]
            cmds.polyAppendVertex(a=num_list)
            #print 'append polygon :'
            self.check_normal_uv()
            if self.last_edge:
                #print 'delete last edge', self.last_edge
                cmds.delete(self.last_edge)
            self.after_edges = common.conv_comp(self.mesh, mode='edge')
            sub_edge = len(self.all_edges)-len(self.after_edges)
            if sub_edge != 0:
                self.sub_edges = self.after_edges[sub_edge:]
            else:
                self.reset_var()
            self.num_list = self.num_list[3:]
            #print 'sub edges :', self.sub_edges
        except Exception as e:
            print e.message, common.location()
            print (traceback.format_exc())
            #print 'append error'
            self.reset_var()
        #選択が3以上なら次の面張りへ再帰
        #print 'try to next append :', self.num_list
        if self.num_list:
            self.apply_append_poly()
        if self.fix_mode.checkedId() == 0:
            self.reset_var()
            
    #法線チェックして必要に応じて反転→UV移動
    def check_normal_uv(self):
        self.check_normal_flag = True
        pre_sel = cmds.ls(sl=True)
        cmds.selectMode(o=True)
        nmv = common.conv_comp(cmds.polyInfo(nmv=True), mode='vtx')
        last_face = common.conv_comp(self.mesh, mode='face')[-1]
        last_vtx = common.conv_comp(last_face, mode='vtx')[-3:]
        #print 'nmv :',nmv
        if nmv:
            #print 'last vtx :', last_vtx
            #フェースの頂点がすべて非多様頂点だった場合は反転する
            if len(list(set(nmv) & set(last_vtx))) == 3:
                #print 'get rev face :', last_face
                if maya_ver <= 2015:#2016以降、コマンドのふるまいが変わっていたので使い分け
                    cmds.polyNormal(last_face, ch=1, normalMode=4)
                else:
                    cmds.polyNormal(last_face, ch=1, normalMode=0)
                    
        self.move_uv(last_face)#UV座標の移動
        cmds.selectMode(co=True)
        cmds.select(pre_sel)
        self.check_normal_flag = False
        
        cmds.polySoftEdge(last_face, a=self.soft_angle.value())
        
    #UV座標の移動
    def move_uv(self, face):
        #print 'uv_edge :', self.uv_edge
        uvs = common.conv_comp(face, mode='uv')#作成したフェースのUV
        edge_uvs = common.conv_comp(self.uv_edge, mode='uv')#選択エッジのUV
        for uv in uvs:
            try:
                vtx = common.conv_comp(uv, mode='vtx')#UVが属する頂点
                vtx_uv = common.conv_comp(vtx, mode='uv')#頂点の共有UVたち
                target_uv = list((set(vtx_uv) & set(edge_uvs)) - set(uvs))
                #print 'get_target uv', target_uv, uv
                pos = cmds.polyEditUV(target_uv[0], query=True)
                cmds.polyEditUV(uv, u=pos[0], v=pos[1], r=False)
            except:
                return
        #UVつないでおく
        face_edges = common.conv_comp(face, mode='edge')
        cmds.polyMergeUV(face_edges, ch=1, d=0.01)
        
    #最後に張られたエッジの位置から適正な始点を返す
    def check_last_edge(self):
        try:
            if len(self.sub_edges) == 1:
                #print 'check last edge'
                self.last_edge = self.sub_edges[0]
            else:
                #print 'check last edge'
                dist = float('inf')
                pos_a = om.MPoint(cmds.pointPosition(self.mesh[0]+'.vtx['+str(self.num_list[0])+']', w=True))
                for e in self.sub_edges:
                    e_vtx = common.conv_comp(e, mode='vtx')
                    #print 'checking last :', e, e_vtx
                    pos_b = om.MPoint(cmds.pointPosition(e_vtx[0], w=True))
                    pos_c = om.MPoint(cmds.pointPosition(e_vtx[1], w=True))
                    l = (pos_a-pos_b).length() + (pos_a-pos_c).length()
                    if dist < l:
                        continue
                    self.last_edge = e
                    dist = l
            self.uv_edge = self.edge+[self.last_edge]
            #print 'get last edge:', self.last_edge
            last_num = self.vtx2num(common.conv_comp(self.last_edge, mode='vtx'))
            for num in self.num_list[:]:
                if num in last_num:
                    continue
                else:
                    last_num.append(num)
            self.num_list = last_num
            #print 'new num_list :',self.num_list
        except Exception as e:
            print e.message, common.location()
            print (traceback.format_exc())
            #print 'check edge error'
        
        
    #初期値リセット
    def reset_var(self):
        #print 'reset_var'
        self.num_list = []
        self.sub_edges = []
        self.last_edge = []
        self.pre_mesh = self.mesh
        self.all_edges = common.conv_comp(self.mesh, mode='edge')
        self.after_edges = copy.copy(self.all_edges)
            
    #頂点情報を数値に変換して戻す
    def vtx2num(self, vtx):
        num_list=[int(v[v.find('[')+1:-1]) for v in vtx]
        return num_list