# Copyright (C) 2016-2022  EDF R&D
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
#

import sys, pickle, tempfile, shutil
from os import path, getpid, environ, remove, system

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from . import utilityFunctions as uF
from . import genereCrack, Zset, output, zcracks_ui

from .output import message, init
from .zcracks_ui import Ui_Zui


#  ---------------------
#    FONCTIONS ANNEXES
#  ---------------------


def stringToFloat(string, typ=float):
  if str(string).replace(' ','')=='':
    out=[]
  else:
    out=list(map(typ, str(string).split()))
  return(out)

def addExtension(string, extension):
  cond=True
  strLen=len(string)
  if strLen<1:
    out=None
  else:
    start=0
    lastPt=0
    while cond:
      res=string.find('.',start)
      if res==-1:
        cond=False
      else:
        lastPt=res
        start=res+1
    if strLen<=(lastPt+1+5) and lastPt!=0:
      out=string[:(lastPt+1)]+extension.replace('.','')
    else:
      out=string+'.'+extension.replace('.','')
  return(out)


#  ----------------------------
#    DEFINITION DE LA CLASSE
#  ----------------------------
class ShipHolderApplication(QGroupBox):

  def __init__(self, parent=None):
    super (ShipHolderApplication, self).__init__(parent)

    self.salomeVers=path.normpath(environ['ROOT_SALOME'])
    self.salomeVers=path.split(self.salomeVers)[-1].split('V')[-1]

    self.createWidgets()
    self.data=dict()
    self.GroupToLoad=None
    self.ui.widget.setVisible(False)
    self.tmpdir=tempfile.mkdtemp()
    self.saneGeoName=path.join(self.tmpdir,'salome_sane.geo')
    self.crackGeoName=path.join(self.tmpdir,'salome_crack.geo')
    self.crackMedName=path.join(self.tmpdir,'salome_crack.med')
    self.crackedGeoName=path.join(self.tmpdir,'cracked.geo')

    global log
    init(self.tmpdir)

    self.verbose=1

    self.ui.CBQuad.toggled.connect(self.pressQuad)
    self.ui.btReset.clicked.connect(self.pressReset)
    self.ui.btCancel.clicked.connect(self.pressCancel)
    self.ui.btApply.clicked.connect(self.pressApply)
    self.ui.btApplyClose.clicked.connect(self.pressApplyClose)
    self.ui.btLoad.clicked.connect(self.pressCharger)
    self.ui.btSave.clicked.connect(self.pressSauver)
    self.ui.btLoadCracked.clicked.connect(self.pressLoadCracked)
    self.ui.btLoadSane.clicked.connect(self.pressLoadSane)

    self.ui.btGrVol.clicked.connect(self.pressLoadGroupVOL)
    self.ui.btGrFace.clicked.connect(self.pressLoadGroupFACE)
    self.ui.btGrEdge.clicked.connect(self.pressLoadGroupEDGE)
    self.ui.btGrNode.clicked.connect(self.pressLoadGroupNODE)
    self.ui.btGrAll.clicked.connect(self.pressLoadGroupALL)
    self.ui.btVisu.clicked.connect(self.pressVisu)

    self.ui.CBAdvanced.toggled.connect(self.pressAdvanced)
    self.lineEditTypes=[str, str, float,
                        float, float, str,
                        str, str, str,
                        float, int, int,
                        str]

    self.lineEditNames=['crackedName','saneName','minSize',
                        'maxSize','extractLength','grVol',
                        'grFace','grEdge','grNodes',
                        'gradation','iterations','layers',
                        'surfopt']

    self.lineEditObjects=[self.ui.valCrackedName,self.ui.valSaneName,self.ui.valMinSize,
                     self.ui.valMaxSize,self.ui.valExtractLength,self.ui.valGrVol,
                     self.ui.valGrFace,self.ui.valGrEdge,self.ui.valGrNode,
                     self.ui.valGradation,self.ui.valIterations,self.ui.valLayers,
                     self.ui.valSurfopt]

  def createWidgets(self):
    self.ui = Ui_Zui()
    self.ui.setupUi(self)

#  -----------------------------------
#    FONCTIONS D'ACTIONS DES BOUTONS
#  -----------------------------------

  def pressQuad(self):
    if self.ui.CBQuad.isChecked():
      self.ui.CBBarsoum.setEnabled(True)
      self.ui.valGradation.setText('2.3')
    else:
      self.ui.valGradation.setText('1.3')
      self.ui.CBBarsoum.setChecked(False)
      self.ui.CBBarsoum.setEnabled(False)


  def pressReset(self):
    for val in self.lineEditObjects:
      val.clear()
    self.ui.CBQuad.setChecked(False)
    self.ui.CBBarsoum.setChecked(False)
    nbOnglet=self.ui.tabWidget.count()
    for iongl in range(nbOnglet):
      onglet=self.ui.tabWidget.widget(iongl)
      tab=onglet.findChildren(QTableWidget)[0]
      for irow in range(tab.rowCount()):
        if tab.item(irow,0) != None:
          tab.item(irow,0).setText('')
    self.ui.valGradation.setText('1.3')
    self.ui.valLayers.setText('6')
    self.ui.valIterations.setText('2')
    self.ui.CBIs2D.setChecked(False)
    self.ui.CBRefine.setChecked(False)


  def pressApply(self):
    message('M','\n\n -------------')
    message('M','  Nouveau cas  ')
    message('M',' -------------')
    message('M','Getting parameters and checking ...')
    self.getParameters()
    test=uF.check(self.data)
    self.cleanTmpFiles()

    if test:
      message('M','Parameters checked and ready to go')
    else:
      message('E','Parameters checking failled',goOn=True)
      return()

    message('M','\nGenerating crack ...')
    res=genereCrack.main(self.data, self.crackMedName)
    if res:
      message('M','Crack generated successfully')
    else:
      message('E','Crack generation failed',goOn=True)
      return()

    if self.ui.CBIs2D.isChecked():
      res=Zset.medToGeo(self.data['saneName'],self.saneGeoName, self.tmpdir, verbose=self.verbose, opt=['  **to_3d'])
    else:
      res=Zset.medToGeo(self.data['saneName'],self.saneGeoName, self.tmpdir, verbose=self.verbose)

    if res!=0:
      message('E','medToGeo sane failed',goOn=True)
      return()

    #opt=['**elset crack *function 1.;','**elset elset0 *function 1.;']
    res=Zset.medToGeo(self.crackMedName,self.crackGeoName, self.tmpdir, verbose=self.verbose)
    if res!=0:
      message('E','medToGeo crack failed',goOn=True)
      return()

    names={'saneGeoName':self.saneGeoName, 'crackGeoName':self.crackGeoName, 'crackedGeoName':self.crackedGeoName}
    message('M','\nInserting crack ...')
    res=Zset.insertCrack(self.data, names, self.tmpdir, verbose=self.verbose)
    if res!=0:
      message('E','Crack insertion failed',goOn=True)
      return()
    else:
      message('M','Crack inserted successfully')

    if self.ui.CBQuad.isChecked() and self.ui.CBBarsoum.isChecked():
      message('M','\nSaving cracked mesh in quadratic with Barsoum elements...')
      opt=['  **lin_to_quad','  **crack_3d_quarter_nodes','   *liset FRONT0']
      res=Zset.geoToMed(self.data['crackedName'], names['crackedGeoName'], self.tmpdir, opt=opt, verbose=self.verbose)

    elif self.ui.CBQuad.isChecked() and not self.ui.CBBarsoum.isChecked():
      message('M','\nSaving cracked mesh in quadratic...')
      opt=['  **lin_to_quad']
      res=Zset.geoToMed(self.data['crackedName'], names['crackedGeoName'], self.tmpdir, opt=opt, verbose=self.verbose)

    else:
      message('M','\nSaving cracked mesh...')
      res=Zset.geoToMed(self.data['crackedName'], names['crackedGeoName'], self.tmpdir, verbose=self.verbose)

    uF.extendElsets(self.data['crackedName'])

    if res==0:
      message('M','Cracked mesh ready at : %s' %(self.data['crackedName']))
      message('M','Maximal aspect ratio is %f' %(uF.getMaxAspectRatio(self.tmpdir)))
      #message('M','medit %s/_mesh_out_to_ghs3d.mesh' %(self.tmpdir))
      message('M','\n ----------------')
      message('M','    Fin cas OK   ')
      message('M',' ----------------')



  def pressApplyClose(self):
    self.pressApply()
    self.pressCancel()


  def pressLoadCracked(self):
    fileDiag = QFileDialog(self)
    fileDiag.setFileMode(QFileDialog.AnyFile)
    fileDiag.setNameFilters(["Parametres *.med (*.*med)","All files (*)"])
    fileDiag.setViewMode(QFileDialog.List)
    if fileDiag.exec_() :
      fileNames = fileDiag.selectedFiles()
      filedef = fileNames[0]
      filedef = addExtension(str(filedef), 'med')
      self.ui.valCrackedName.setText(filedef)


  def pressLoadSane(self):
    fileDiag = QFileDialog(self)
    fileDiag.setFileMode(QFileDialog.AnyFile)
    fileDiag.setNameFilters(["Parametres *.med (*.*med)","All files (*)"])
    fileDiag.setViewMode(QFileDialog.List)
    if fileDiag.exec_() :
      fileNames = fileDiag.selectedFiles()
      filedef = fileNames[0]
      self.ui.valSaneName.setText(filedef)


  def pressCharger(self):
    fileDiag = QFileDialog(self)
    fileDiag.setFileMode(QFileDialog.AnyFile)
    fileDiag.setNameFilters(["Parametres *.dic (*.dic)","All files (*)"])
    fileDiag.setViewMode(QFileDialog.List)

    if fileDiag.exec_() :
      fileNames = fileDiag.selectedFiles()
      filedef = fileNames[0]
      if not path.isfile(str(filedef)):
        message('E','Invalid dic file')
      self.data=pickle.load(open(str(filedef),'r'))
      message('M','\nLoading parameters from %s' %str(filedef))

      for cont, obj in enumerate(self.lineEditObjects):
        if self.lineEditTypes[cont] in [float, int]:
          obj.setText(self.data['TXT'+self.lineEditNames[cont]])
        else:
          obj.setText(self.data[self.lineEditNames[cont]])

      self.ui.CBQuad.setChecked(True if 'quad' in list(self.data.keys()) and self.data['quad'] else False)
      self.ui.CBBarsoum.setChecked(True if 'barsoum' in list(self.data.keys()) and self.data['barsoum'] else False)
      self.ui.CBIs2D.setChecked(True if 'is2D' in list(self.data.keys()) and self.data['is2D'] else False)
      self.ui.CBRefine.setChecked(True if 'refine' in list(self.data.keys()) and self.data['refine'] else False)

      self.setTableParameters()


  def pressSauver(self):
    fileDiag = QFileDialog(self)
    fileDiag.setFileMode(QFileDialog.AnyFile)
    fileDiag.setNameFilters(["Parametres *.dic (*.dic)","All files (*)"])
    fileDiag.setViewMode(QFileDialog.List)
    if fileDiag.exec_() :
      self.getParameters()
      fileNames = fileDiag.selectedFiles()
      filedef = fileNames[0]
      pickle.dump(self.data, open(addExtension(str(filedef), 'dic'),'w'))
      message('M','Saving parameters in %s' %addExtension(str(filedef), 'dic'))

  def pressLoadGroupVOL(self):
    try:
      self.GroupToLoad='VOL'
      self.loadGroups()
    except:
      message('E','Groups loading impossible',goOn=True)

  def pressLoadGroupFACE(self):
    try:
      self.GroupToLoad='FACE'
      self.loadGroups()
    except:
      message('E','Groups loading impossible',goOn=True)

  def pressLoadGroupEDGE(self):
    try:
      self.GroupToLoad='EDGE'
      self.loadGroups()
    except:
      message('E','Groups loading impossible',goOn=True)

  def pressLoadGroupNODE(self):
    try:
      self.GroupToLoad='NODE'
      self.loadGroups()
    except:
      message('E','Groups loading impossible',goOn=True)

  def pressLoadGroupALL(self):
    try:
      self.GroupToLoad='ALL'
      self.loadGroups()
    except:
      message('E','Groups loading impossible',goOn=True)

  def pressAdvanced(self):
    if self.ui.CBAdvanced.isChecked():
      self.ui.widget.setVisible(True)
    else:
      self.ui.widget.setVisible(False)

  def pressVisu(self):
    meshFile1=path.join(self.tmpdir,'_mesh_out_to_ghs3d.mesh')
    meshFile2=path.join(self.tmpdir,'_mesh_out_.mesh')
    test1=path.isfile(meshFile1)
    test2=path.isfile(meshFile2)
    medit=path.join('$Z7PATH/PUBLIC/lib-Linux_64/Zmesh/bin/medit')
    if not test1:
      if not test2:
        message('A','No mesh file to visualize')
      else:
        print(medit+' %s' %meshFile2)
        system(medit+' %s' %meshFile2)
    else:
      print(medit+' %s' %meshFile1)
      system(medit+' %s' %meshFile1)
    return()

  def pressCancel(self):
    message('M','exiting Zcracks')
    try:
      shutil.rmtree(self.tmpdir)
    except:
      message('E','Impossible to delete %s' %self.tmpdir,goOn=True)
      pass
    exit()

#  ---------------------------------
#    FONCTIONS ANNEXES A LA CLASSE
#  ---------------------------------


  def getParameters(self):
    for cont, name in enumerate(self.lineEditNames):
      value=str(self.lineEditObjects[cont].text())
      #print name
      if self.lineEditTypes[cont] == float:
        self.data['TXT'+name]=value
        self.data[name]= stringToFloat(value)
      elif self.lineEditTypes[cont] == int:
        self.data['TXT'+name]=value
        self.data[name]= stringToFloat(value, typ=int)
      else:
        self.data[name]=value
    self.data['quad']=self.ui.CBQuad.isChecked()
    self.data['barsoum']=self.ui.CBBarsoum.isChecked()
    self.data['TXTcrack']=self.getTableParameters()
    self.data['crack']=self.getTableParameters(str2float=True)
    self.data['is2D']=self.ui.CBIs2D.isChecked()
    self.data['refine']=self.ui.CBRefine.isChecked()


  def getTableParameters(self, str2float=False):
    nbOnglet=self.ui.tabWidget.count()
    out=dict()
    iOngletActif=self.ui.tabWidget.currentIndex()
    ongletActif=False
    for iongl in range(nbOnglet):
      crack=dict()
      onglet=self.ui.tabWidget.widget(iongl)
      tab=onglet.findChildren(QTableWidget)[0]
      for irow in range(tab.rowCount()):
        label=tab.verticalHeaderItem(irow).text()
        if tab.item(irow,0) is None:
          crack[str(label)]=''
        elif 'med file' in str(label):
          crack[str(label)]=str(tab.item(irow,0).text())
        else:
          value=tab.item(irow,0).text()
          if str2float:
            crack[str(label)]=stringToFloat(value)
          else:
            crack[str(label)]=str(value)
      out[str(self.ui.tabWidget.tabText(iongl))]=crack
      if iongl==iOngletActif:
        ongletActif=str(self.ui.tabWidget.tabText(iongl))

    out['actif']=ongletActif
    return(out)


  def setTableParameters(self):
    nbOnglet=self.ui.tabWidget.count()
    #iOngletActif=self.ui.tabWidget.currentIndex()
    for iongl in range(nbOnglet):
      onglet=self.ui.tabWidget.widget(iongl)
      tab=onglet.findChildren(QTableWidget)[0]
      for irow in range(tab.rowCount()):
        label=tab.verticalHeaderItem(irow).text()
        if tab.item(irow,0) == None:
          item = QTableWidgetItem()
          tab.setItem(irow, 0, item)
        tab.item(irow,0).setText(self.data['TXTcrack'][str(self.ui.tabWidget.tabText(iongl))][str(label)])
      if str(self.ui.tabWidget.tabText(iongl)) == self.data['TXTcrack']['actif']:
        self.ui.tabWidget.setCurrentWidget(onglet)


  def loadGroups(self):
    saneFile=str(self.ui.valSaneName.text())
    message('I','Loading Sane mesh...')
    if not path.isfile(saneFile):
      message('E','Sane mesh med file is not valid')
    else:
      import SMESH, salome
      #salome.salome_init()
      from salome.smesh import smeshBuilder
      smesh = smeshBuilder.New()

      ([objetSain], status) = smesh.CreateMeshesFromMED(saneFile)

      groupsVOL, groupsFAC, groupsEDG, groupsNOD = '', '', '', ''
      nGr=0

      for group in objetSain.GetGroups():
        if (self.GroupToLoad in ['VOL','ALL']) and (group.GetType()==SMESH.VOLUME):
          groupsVOL+=self.cleanGroupName(group)
          nGr+=1

        if (self.GroupToLoad in ['FACE','ALL']) and (group.GetType()==SMESH.FACE):
          groupsFAC+=self.cleanGroupName(group)
          nGr+=1

        if (self.GroupToLoad in ['EDGE','ALL']) and (group.GetType()==SMESH.EDGE):
          groupsEDG+=self.cleanGroupName(group)
          nGr+=1

        if (self.GroupToLoad in ['NODE','ALL']) and (group.GetType()==SMESH.NODE):
          groupsNOD+=self.cleanGroupName(group)
          nGr+=1

      if groupsVOL!='':  self.ui.valGrVol.setText(groupsVOL)
      if groupsFAC!='': self.ui.valGrFace.setText(groupsFAC)
      if groupsEDG!='': self.ui.valGrEdge.setText(groupsEDG)
      if groupsNOD!='': self.ui.valGrNode.setText(groupsNOD)

      message('I','%d group(s) found' %nGr)

  def cleanTmpFiles(self):
    for f in [self.saneGeoName, self.crackGeoName, self.crackMedName, self.crackedGeoName]:
      try:
        remove(f)
      except:
        pass
        
  def cleanGroupName(self, group):
    name=group.GetName()
    while name.endswith(' '): name=name[:-1]
    if ' ' in name: 
      message('A','%s group has a space in its name --> ignored' %name)
      return('')
    else:
      return(name+" ")
      
  def checkNamesSpaces(self, names):
    if type(names) is str: names=[names]
    ok=True
    for n in names:
      if ' ' in n:
        message('E','%s has a space in its name, please remove it' %n, goOn=True)
        ok=False
    return(ok)


#  ---------------------------------
#    LANCEMENT DE LA BOITE DE DIAG
#  ---------------------------------




