# =============================================================================
#
#  Copyright (c) Kitware, Inc.
#  All rights reserved.
#  See LICENSE.txt for details.
#
#  This software is distributed WITHOUT ANY WARRANTY; without even
#  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#  PURPOSE.  See the above copyright notice for more information.
#
# =============================================================================

"""SetupPaths operation

Sets variables on smtk for use by python operations
    smtk.workflows_folder   file system location of simulation-workflows folder
    smtk.operations_folder  file system location of python operation source files
    smtk.use_openfoam_docker
    smtk.container_engine   optionally set to string, e.g. 'docker' or 'podman', or full path
    smtk.uid                user id on docker machine
    smtk.gid                group id on docker machine

Applications should run this operation to set workflows_path and operations_path
*before* running the other operations
"""

import os
import platform
import subprocess
import sys

import smtk
import smtk.attribute
import smtk.io
import smtk.operation

template_string = """
<?xml version="1.0" encoding="utf-8" ?>
<SMTK_AttributeResource Version="5">
  <Definitions>
    <AttDef Type="setup" BaseType="operation">
      <ItemDefinitions>
        <Directory Name="workflows_folder" NumberOfRequiredValues="1" />
        <Directory Name="operations_folder" NumberOfRequiredValues="1" />
        <String Name="container_engine" NumberOfRequiredValues="1" Optional="true" IsEnabledByDefault="true">
          <DefaultValue>docker</DefaultValue>
        </String>
      </ItemDefinitions>
    </AttDef>

    <AttDef Type="result(setup)" BaseType="result">
      <ItemDefinitions>
        <String Name="uid" NumberOfRequiredValues="1" />
        <String Name="gid" NumberOfRequiredValues="1" />
      </ItemDefinitions>
    </AttDef>
  </Definitions>
</SMTK_AttributeResource>
"""


class SetupPaths(smtk.operation.Operation):
    """Sets variables on smtk for use by python operations.

    Always sets
        smtk.workflows_folder   file system location of simulation-workflows folder
        smtk.operations_folder  file system location of python operation source files
    Optionally sets (if container_engine item enabled)
        smtk.use_openfoam_docker
        smtk.container_engine
        smtk.uid                user id on docker machine
        smtk.gid                group id on docker machine
    """

    def __init__(self):
        smtk.operation.Operation.__init__(self)

    def name(self):
        return "SetupPaths"

    def createSpecification(self):
        spec = self.createBaseSpecification()

        reader = smtk.io.AttributeReader()
        hasErr = reader.readContents(spec, template_string, self.log())
        if hasErr:
            message = 'Error loading SetPaths specification'
            self.log().addError(message)
            raise RuntimeError(message)
        return spec

    def operateInternal(self):
        """"""
        # Get workflows folder
        workflows_item = self.parameters().findDirectory('workflows_folder')
        workflows_folder = workflows_item.value()
        if not os.path.exists(workflows_folder):
            self.log().addError('workflows_folder not found: {}'.format(workflows_folder))
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)
        smtk.workflows_folder = workflows_folder
        self.log().addDebug('workflows_folder: {}'.format(smtk.workflows_folder))

        # Get operations folder
        operations_item = self.parameters().findDirectory('operations_folder')
        operations_folder = operations_item.value()
        if not os.path.exists(operations_folder):
            self.log().addError('operations_folder not found: {}'.format(operations_folder))
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)
        smtk.operations_folder = operations_folder
        self.log().addDebug('operations_folder: {}'.format(smtk.operations_folder))

        container_engine_item = self.parameters().findString('container_engine')
        if not container_engine_item.isEnabled() or not container_engine_item.value():
            msg = 'Container engine not specified so NOT using openfoam docker image'
            self.log().addRecord(smtk.io.Logger.Info, msg)
        else:
            smtk.use_openfoam_docker = True
            smtk.container_engine = container_engine_item.value()
            self.log().addRecord(smtk.io.Logger.Info, 'Using container engine: {}'.format(smtk.container_engine))

            # Get user and group id
            if platform.system().lower().startswith('windows'):
                # For Windows, get WSL info. Our understanding is that docker containers run in a VM
                # that is analogous to WSL. So get WSL ids by running "bash" command in powershell.
                script = ['powershell', 'bash', '-c', ' "id -u" ']
                result = subprocess.run(script, capture_output=True, text=True,
                                        creationflags=subprocess.CREATE_NO_WINDOW)
                if result.returncode != 0:
                    self.log().addRecord(smtk.io.Logger.Info, result.stdout)
                    self.log().addError(result.stderr)
                    return self.createResult(smtk.operation.Operation.Outcome.FAILED)
                smtk.uid = result.stdout.strip()

                script[-1] = ' "id -g" '
                result = subprocess.run(script, capture_output=True, text=True,
                                        creationflags=subprocess.CREATE_NO_WINDOW)
                if result.returncode != 0:
                    self.log().addRecord(smtk.io.Logger.Info, result.stdout)
                    self.log().addError(result.stderr)
                    return self.createResult(smtk.operation.Operation.Outcome.FAILED)
                smtk.gid = result.stdout.strip()
            else:
                # For linux and macOS, use the info from the native OS
                smtk.uid = str(os.getuid())
                smtk.gid = str(os.getgid())

            self.log().addRecord(smtk.io.Logger.Info, 'uid {} gid {}'.format(smtk.uid, smtk.gid))

        result = self.createResult(smtk.operation.Operation.Outcome.SUCCEEDED)
        if smtk.container_engine is not None:
            result.findString('uid').setValue(smtk.uid)
            result.findString('gid').setValue(smtk.gid)

        return result
