#              COPYRIGHT ANSYS. ALL RIGHTS RESERVED.              
# This file contains proprietary software licensed from ANSYS Inc.
# This header must remain in any source code despite modifications or
# enhancements by any party.

import os, json, clr, Ansys
import socket, SocketServer, base64, threading, zipfile
clr.AddReference("Ans.Core")
clr.AddReference("Ans.ProjectSchematic")
import Ansys.ProjectSchematic


def getProjectName():
    return os.path.basename(Project.GetProjectFile())
        
        
def IsEntireProjectUpToDate():
    upToDate = True
    includeParametricSystems = True
    if not includeParametricSystems:
        upToDate = IsProjectUpToDate()
    else:
        systems = GetAllSystems()
        components = []
        for system in systems:
            components.extend(system.Components)
        
        for c in components:
            state = GetSystemComponentState(c).State
            if state != Ansys.ProjectSchematic.State.UpToDate:
                upToDate = False
                break
        
        if upToDate:
            for c in components:
                state = GetComponentState(c).State
                if state != Ansys.ProjectSchematic.State.UpToDate:
                    upToDate = False
                    break
    
    return upToDate


def exportReportToWorkingDirectory():
    cwd = os.getcwd()
    path = os.path.join(cwd, 'report', 'report.xml')
    ExportReport(FilePath=path)


def exportReportArchive():
    cwd = os.getcwd()
    xmlPath = os.path.join(cwd, 'report.xml')
    ExportReport(FilePath=xmlPath)
    zip = zipfile.ZipFile('report.zip', 'w')
    zip.write(xmlPath, 'report.xml')
    imagesFolderPath = os.path.join(cwd, 'report_images')
    for root, dirs, files in os.walk(imagesFolderPath):
        for f in files:
            path = os.path.join(root, f)
            pathRelativeToWorkingDir = path[len(cwd)+1:]
            zip.write(path, pathRelativeToWorkingDir)
    zip.close()

    
def getReport():
    exportReportArchive()
    encodedString = ''
    with open('report.zip', 'rb') as f:
        contents = f.read()
        encodedString = base64.b64encode(contents)
    return encodedString


def getMessageData():
    msgDataList = []
    for m in Project.GetMessages():
        msgDataList.append({'type': m.MessageType, 'summary': m.Summary, 'association': m.Association, 'timeStamp': str(m.DateTimeStamp)})
    return json.dumps(msgDataList)


def getParameterData():
    paramDataList = []
    for p in Parameters.GetAllParameters():
        val = p.Value
        value = ''
        unit = ''
        if val is not None:
            try:
                value = val.Value
                unit = val.Unit
            except:
                value = val
            if type(value) == float:
                value = "%.5g" % value
            else:
                value = '{0}'.format(value)
        paramDataList.append({'name': p.Name, 'displayText': p.DisplayText, 'usage': p.Usage, 'value': value, 'unit': unit, 'expression': p.Expression, 'expressionType': p.ExpressionType, 'errorMessage': p.ErrorMessage, 'quantityName': p.ValueQuantityName})
    return json.dumps(paramDataList)


def getDesignPointData():
    dpDataList = []
    for dp in Parameters.GetAllDesignPoints():
        upToDate = 'UpToDate'==dp.GetEntityProperty(Name='StateOfParameters')
        dpDataList.append({'name': dp.Name, 'displayText': dp.DisplayText, 'note': dp.Note, 'retained': dp.Retained, 'retainedDataValid': dp.HasValidRetainedData, 'upToDate': upToDate})
    return json.dumps(dpDataList)


def getParamValues(dpName):
    dp = Parameters.GetDesignPoint(Name=dpName)
    paramValueList = []
    for p in Parameters.GetAllParameters():
        if dp.GetParameterValue(p) is None:
            paramValueList.append('')
        else:
            try:
                value = dp.GetParameterValue(p).Value
            except:
                value = dp.GetParameterValue(p)
            if type(value) == float:
                value = "%.5g" % value
            else:
                value = '{0}'.format(value)
            paramValueList.append(value)
    return json.dumps(dict(zip([p.Name for p in Parameters.GetAllParameters()], paramValueList)))


def saveMonitoringData(path):
    data = {}
    data['messages'] = getMessageData()
    data['parameters'] = getParameterData()
    data['dpMetadata'] = getDesignPointData()
    data['dpData'] = {}
    for dp in Parameters.GetAllDesignPoints():
        dpName = dp.Name
        data['dpData'][dpName] = getParamValues(dpName)
    f = open(path, 'w')
    f.write(json.dumps(data))
    f.close()


def createDesignPoint():
    dp = Parameters.CreateDesignPoint(IsRetained=False)
    return dp.Name
    
    
def duplicateDesignPoint(dpName):
    dp = Parameters.GetDesignPoint(Name=dpName)
    dp2 = dp.Duplicate()
    return dp2.Name
    
    
def deleteDesignPoint(dpName):
    dp = Parameters.GetDesignPoint(Name=dpName)
    Parameters.DeleteDesignPoint(dp)
    
    
def setDpParameter(dpName, paramName, paramValue):
    dp = Parameters.GetDesignPoint(Name=dpName)
    p = Parameters.GetParameter(Name=paramName)
    dp.SetParameterExpression(Parameter=p, Expression=paramValue)


def setDpNote(dpName, dpNote):
    dp = Parameters.GetDesignPoint(Name=dpName)
    dp.Note = dpNote


def setDpRetained(dpName, retained):
    dp = Parameters.GetDesignPoint(Name=dpName)
    dp.Retained = retained


def canAbortUpdate():
    try:
        return __pmvar__.CanAbort
    except:
        return False


def getProgressData():
    progressData = []
    try:
        if __pmvar__.ProgressStatus == Ansys.Core.ProgressMonitoring.ProgressStatus.Running:
            progressData = [{'taskStatus': __pmvar__.TaskStatus, 'details': __pmvar__.TaskDetails, 'progressStatus': str(__pmvar__.ProgressStatus), 'percentComplete': __pmvar__.CompletedWorkUnits/__pmvar__.TotalWorkUnits*100}]
    except:
        pass
    return json.dumps(progressData)
	

def getAllMonitors():
    monitorPostfixList = ['']
    for i in range(1, 1000):
        monitorPostfixList.append(' ' + str(i)) # ['', ' 1', ' 2',.., ' 100']

    allSolutionMonitors = []
    for i in monitorPostfixList:
        try:
            sm = GetDataEntity("/ASim/SolutionMonitor:SolutionMonitor" + i)
            allSolutionMonitors.append(sm)
        except:
            continue
    return allSolutionMonitors


def getMonitorCount():
    return len(getAllMonitors())


def getMonitorData(dataFromIndex=0):
    allMonitorsData = []
    for sm in getAllMonitors():
        monitoredVariables = sm.SolutionOutput
        variables = ['Iteration']
        variables.extend(monitoredVariables)
        monitorData = []
        if dataFromIndex == 0: # Add header only on the first call; not on subsequent calls
            monitorData = [variables]
        iterData = None
        varData = {}
        for mv in monitoredVariables:
            if iterData is None:
                xdata = ASim.GetSolutionData(sm, "Cum Iter", mv) # has number (value) + unit
                iterData = [int(xdata[i].Value) for i in range(dataFromIndex, len(xdata))]
                varData['iteration'] = iterData
            ydata = []
            if mv == 'kFORCE_CONVERGENCE':
                ydata = ASim.GetSolutionData(sm, 'F    L2 ', mv)
            elif mv == 'kDISP_CONVERGENCE':
                ydata = ASim.GetSolutionData(sm, 'U    L2 ', mv)
            elif mv == 'kMOMENT_CONVERGENCE':
                ydata = ASim.GetSolutionData(sm, 'M    L2 ', mv)
            elif mv == 'kROTATION_CONVERGENCE':
                ydata = ASim.GetSolutionData(sm, 'ROT  L2 ', mv)
            varData[mv] = [round(float(ydata[i].Value),3) for i in range(dataFromIndex, len(ydata))]
        for i in range(len(iterData)):
            row = [iterData[i]]
            for mv in monitoredVariables:
                row.append(varData[mv][i])
            monitorData.append(row)
        allMonitorsData.append(monitorData)
    monitorDataJson = json.dumps(allMonitorsData)
    return monitorDataJson


def setAllComponentUpdatesToForeground():
    for system in Project.GetAllSystems():
        try:
            solution = system.GetContainer(ComponentName="Solution")
            if "GetSolutionSettings" in dir(solution):
                settings = solution.GetSolutionSettings()
                settings.UpdateOption = "Foreground"
        except:
            pass


def setDesignPointUpdateSettings(executionMode, solveManager, queue, cores, maxJobs):
    settings = GetDesignPointUpdateSettings()
    settings.JobControl = "OneJobForGroupDesignPoint"
    settings.MaxNumberOfJobs = maxJobs
    settings.ComponentExecutionMode = executionMode
    setAllComponentUpdatesToForeground()
    if executionMode == 'Serial':
        settings.UpdateOption = "Foreground"
    else:
        settings.UpdateOption = "RemoteSolveManager"
        settings.SolveManager = solveManager
        settings.Queue = queue
        settings.NumberOfCoresPerJob = cores


def getDesignPointUpdateSettings():
    settings = GetDesignPointUpdateSettings()
    data = {}
    if len(settings.ComponentExecutionMode) > 0:
        data['componentExecutionMode'] = settings.ComponentExecutionMode[0];
    if len(settings.Queue) > 0:
        data['updateQueue'] = settings.Queue[0]
    data['maxNumberOfJobs'] = settings.MaxNumberOfJobs
    data['numberOfCores'] = settings.NumberOfCoresPerJob
    data['parametric'] = len(Parameters.GetAllParameters()) > 0
    return json.dumps(data)



