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

import io
import os

import smtk

from card_format import CardFormat


class ControlDictWriter:

    def generate_controlDict(self, case_dir: str, att_resource: smtk.attribute.Resource) -> bool:
        """Writes controlDict file to case_dir/system. Returns True on success."""
        control_att = att_resource.findAttribute('controlDict')

        complete = False
        path = os.path.join(case_dir, 'system/controlDict')
        # print('PATH', path)
        with open(path, 'w') as fp:
            self.write_foamfile_header(fp, 'controlDict')

            fp.write('// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n')

            # Get application
            app_name = self.get_application_name(att_resource)
            t = ('application', app_name)
            kv_list = [t]

            # Get rest of controlDict settings and write
            kv_list += self._get_control_data(control_att)
            self.write_kvlist(fp, kv_list)

            fp.write('\n')
            fp.write('// ************************************************************************* //\n')
            complete = True
        return complete

    def get_application_name(self, att_resource: smtk.attribute.Resource) -> str:
        """Looks up the OpenFOAM solver name in the Analysis attribute."""
        att = att_resource.findAttribute('Analysis')
        if att is None:
            return 'NoAnalysisAttribute'
        item = att.findString('Analysis')
        if item is None:
            return 'NoAnalysisItem'
        return item.value()

    def write_foamfile_header(self, fp: io.TextIOWrapper, name: str):
        """"""
        fp.write('// Automatically Generated \n')
        fp.write('FoamFile\n')
        fp.write('{\n')

        fp.write('    {0:<11} {1};\n'.format('version', '2.0'))
        fp.write('    {0:<11} {1};\n'.format('format', 'ascii'))
        fp.write('    {0:<11} {1};\n'.format('class', 'dictionary'))
        fp.write('    {0:<11} {1};\n'.format('object', name))

        fp.write('}\n')

    def write_kvsection(self, fp: io.TextIOWrapper, kv_list: list, name: str):
        """"""
        fp.write('\n{0}\n'.format(name))
        fp.write('{\n')
        self.write_kvlist(fp, kv_list, indent=4)
        fp.write('}\n')

    def write_kvlist(self, fp: io.TextIOWrapper, kv_list: list, column0=15, indent=0, double_space=True):
        """"""
        indent_string = '' if indent == 0 else ' ' * indent
        for i, kv in enumerate(kv_list):
            key, value = kv
            if double_space and i > 0:
                fp.write('\n')

            # Special cases
            if key == '$blank':
                fp.write('\n')
                continue
            elif key == '$comment':
                if not value.startswith('//'):
                    value = '// '.format(value)
                fp.write(value)
                fp.write('\n')
                continue

            length = column0 if len(key) < column0 else len(key)
            # Embed length into format string, e.g., "{:<15} {1};\n"
            format_string = '{0}{{0:<{1}}} {{1}};\n'.format(indent_string, length)
            if isinstance(value, list):
                string_list = [str(v) for v in value]
                inner = ' '.join(string_list)
                value = '({})'.format(inner)
            line = format_string.format(key, value)
            fp.write(line)

    def _get_control_data(self, att: smtk.attribute.Attribute) -> list:
        """Returns list of (key, value) tuples for controlDict items"""
        kv_list = list()

        cards = [
            CardFormat(att, 'startFrom'),
            CardFormat(att, 'startTime', item_path='startFrom/startTime', default=0),
            CardFormat(att, 'stopAt'),
            CardFormat(att, 'endTime', item_path='stopAt/endTime', default=0.5),
            CardFormat(att, 'deltaT'),
            CardFormat(att, 'writeControl'),
            CardFormat(att, 'timeStep'),
            CardFormat(att, 'writeInterval', item_path='writeControl/writeIntervalInt'),
            CardFormat(att, 'writeInterval', item_path='writeControl/writeIntervalDouble'),
        ]
        for card in cards:
            kv = card.to_string_tuple()
            if kv is not None:
                kv_list.append(kv)

        # Hard code the remaining items for now
        kv_list.append(('$blank', None))
        kv_list.append(('$comment', 'Remaining values are currently hard-coded'))

        kv_list.append(('purgeWrite', '0'))
        kv_list.append(('writeFormat', 'ascii'))
        kv_list.append(('writePrecision', '6'))
        kv_list.append(('writeCompression', 'off'))
        kv_list.append(('timeFormat', 'general'))
        kv_list.append(('timePrecision', '6'))
        kv_list.append(('runTimeModifiable', 'true'))

        return kv_list
