# =============================================================================
#
#  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.
#
# =============================================================================

"""CreateProject operation"""

import os
import shutil

import smtk
import smtk.attribute
import smtk.attribute_builder
import smtk.io
import smtk.operation
import smtk.project

OP_SUCCEEDED = int(smtk.operation.Operation.Outcome.SUCCEEDED)
PROJECT_NAME = 'foam.windtunnel'

# Make sure __file__ is set when using modelbuilder
import inspect
source_file = os.path.abspath(inspect.getfile(inspect.currentframe()))
__file__ = source_file


class CreateProject(smtk.operation.Operation):
    """Creates project directory for running OpenFOAM simulation."""

    def __init__(self):
        super(CreateProject, self).__init__()

    def name(self):
        return "create wind tunnel project"

    def operateInternal(self):
        # Set up project directory
        project_folder = self.parameters().findDirectory('directory').value()
        if os.path.exists(project_folder) and len(os.listdir(project_folder)) > 0:
            overwrite = self.parameters().findVoid('overwrite').isEnabled()
            if overwrite:
                shutil.rmtree(project_folder)
            else:
                message = 'Cannot use folder with existing contents: {}'.format(project_folder)
                self.log().addError(message)
                raise RuntimeError(message)

        # Get workflows folder
        if not hasattr(smtk, 'workflows_folder'):
            self.log().addError(
                'smtk is missing workflows_folder. Do you need to set DEVELOPER_MODE?')
            return self.createResult(smtk.operation.Operation.Outcome.UNABLE_TO_OPERATE)

        if not os.path.exists(project_folder):
            os.makedirs(project_folder)

        # Create smtk::project::Project instance
        # Must use Create operation because python ops cannot subclass smtk.project.Operation
        create_op = self.manager().createOperation('smtk::project::Create')
        create_op.parameters().findString('typeName').setValue(PROJECT_NAME)
        create_result = create_op.operate()
        create_outcome = create_result.findInt('outcome').value()
        if create_outcome != OP_SUCCEEDED:
            message = 'Create operation failed with outcome {}'.format(create_outcome)
            self.log().addError(message)
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)
        project = create_result.findResource('resourcesCreated').value()

        name = os.path.split(project_folder)[-1]
        project.setName(name)
        filename = '{}.project.smtk'.format(name)
        location = os.path.join(project_folder, filename)
        project.setLocation(location)

        # Create attribute resource
        sbt_path = os.path.join(smtk.workflows_folder, 'WindTunnel.sbt')
        if not os.path.exists(sbt_path):
            message('Template file not found: {}'.format(sbt_path))
            self.log().addError(message)
            raise RuntimeError(message)

        sbt_op = self.manager().createOperation('smtk::attribute::Import')
        sbt_op.parameters().findFile('filename').setValue(sbt_path)
        sbt_result = sbt_op.operate()
        sbt_outcome = sbt_result.findInt('outcome').value()
        if sbt_outcome != OP_SUCCEEDED:
            message = 'Error importing attribute template file {}'.format(sbt_path)
            self.log().addError(message)
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)
        att_res = sbt_result.findResource('resourcesCreated').value()

        # Create instanced attributes
        builder = smtk.attribute_builder.AttributeBuilder()
        builder.att_resource = att_res  # pending fix to smtk
        builder.create_instanced_atts(att_res)

        role = 'attributes'
        att_res.setName(role)
        filename = '{}.{}.smtk'.format(role, att_res.id())
        path = os.path.join(project_folder, 'resources', filename)
        att_res.setLocation(path)

        project.resources().add(att_res, role)

        # Write to disk
        write_op = self.manager().createOperation('smtk::project::Write')
        write_op.parameters().associate(project)
        write_result = write_op.operate()
        write_outcome = write_result.findInt('outcome').value()
        if (write_outcome != OP_SUCCEEDED):
            self.log().addError('Write project failed, outcome {}'.format(write_outcome))
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)

        # Create root foam directory
        foam_dir = os.path.join(project_folder, 'foam')
        os.makedirs(foam_dir)

        this_result = self.createResult(smtk.operation.Operation.Outcome.SUCCEEDED)
        # Add model resource to results (attribute)
        resource_item = this_result.findResource('resourcesCreated')
        ok = resource_item.setNumberOfValues(1)
        ok &= resource_item.setValue(project)
        if not ok:
            self.log().addError('Failed to assign new resource to results item')
            return self.createResult(smtk.operation.Operation.Outcome.FAILED)
        return this_result

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

        source_dir = os.path.dirname(__file__)
        sbt_path = os.path.join(source_dir, 'create_project.sbt')
        if not os.path.exists(sbt_path):
            message = 'File not found: {}'.format(sbt_path)
            self.log().addError(message)
            raise RuntimeError(message)

        reader = smtk.io.AttributeReader()
        hasErr = reader.read(spec, sbt_path, self.log())
        if hasErr:
            message = 'Error loading specification file: {}'.format(sbt_path)
            self.log().addError(message)
            raise RuntimeError(message)
        return spec
