import argparse
import os
import shutil
import sys

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Generate attribute resource from yml description')
    # Required arguments
    parser.add_argument('template_filepath', help='Attribute template filename/path (.sbt)')
    parser.add_argument('model_filepath', help='path to model file (.gen, .ncdf)')
    parser.add_argument('yml_filepath', help='YAML file specifying the attributes to generate')
    # Optional arguments
    parser.add_argument('-o', '--output_folder', help='output folder for writing resource files')
    parser.add_argument('-s', '--smtktools_path', help='path to smtk_tools module')
    # Todo path to validation spec file
    # parser.add_argument('-s', '--skip_instances', action='store_true', help='skip initializing instanced attributes')

    args = parser.parse_args()
    # print(args)

    # Check filesystem paths
    if not os.path.exists(args.template_filepath):
        raise RuntimeError('template file not found: {}'.format(args.template_filepath))
    if not os.path.exists(args.model_filepath):
        raise RuntimeError('model file not found: {}'.format(args.model_filepath))
    if not os.path.exists(args.yml_filepath):
        raise RuntimeError('yml spec not found: {}'.format(args.yml_filepath))
    if args.smtktools_path is not None and not os.path.exists(args.smtktools_path):
        raise RuntimeError('smtk_tools directory not found: {}'.format(args.smtktools_path))
    if args.output_folder is not None:
        if os.path.exists(args.output_folder):
            print('Deleting existing output folder', args.output_folder)
            shutil.rmtree(args.output_folder)
        os.makedirs(args.output_folder)

    # Add path to smtk_tools
    tools_path = args.smtktools_path
    if tools_path is None:
        # Default path from the repository
        source_path = os.path.abspath(os.path.dirname(__file__))
        path = os.path.join(source_path, os.pardir, os.pardir, 'thirdparty', 'smtk-tools')
        tools_path = os.path.normpath(path)

    assert os.path.exists(tools_path), 'smtk-tools path not found at {}'.format(tools_path)
    sys.path.insert(0, tools_path)

    import smtk_tools
    from smtk_tools.resource_io import ResourceIO
    from smtk_tools.attribute_builder import AttributeBuilder

    # Load yml file as the attribute specification
    import yaml
    spec = None
    print('Loading yaml file:', args.yml_filepath)
    with open(args.yml_filepath) as fp:
        content = fp.read()
        spec = yaml.safe_load(content)
    assert spec is not None

    # Initialize ResourceIO and load resources
    model_resource = None
    res_io = ResourceIO()
    if args.model_filepath:
        # Support model or resource input
        basename, ext = os.path.splitext(args.model_filepath)
        if ext == '.smtk':
            print('Loading model resource:', args.model_filepath)
            model_resource = res_io.read_resource(args.model_filepath)
        else:
            print('Importing model file:', args.model_filepath)
            model_resource = res_io.import_resource(args.model_filepath)
        assert model_resource is not None, 'failed to load model file {}'.format(args.model_filepath)
    print('Importing attribute template', args.template_filepath)
    att_resource = res_io.import_resource(args.template_filepath)
    assert att_resource is not None, 'failed to import attribute template from {}'.format(args.template_filepath)

    # Associate the model resource
    if model_resource is not None:
        att_resource.associate(model_resource)

    # Initialize builder and populate the attributes
    builder = AttributeBuilder()
    builder.build_attributes(att_resource, spec, model_resource=model_resource)

    # Write the resource files
    if args.output_folder:
        att_path = os.path.join(args.output_folder, 'attributes.smtk')
        res_io.write_resource(att_resource, att_path)
        print('Wrote', att_path)

        # Only write model resource if it was imported
        basename, ext = os.path.splitext(args.model_filepath)
        if ext != '.smtk':
            model_path = os.path.join(args.output_folder, 'model.smtk')
            res_io.write_resource(model_resource, model_path)
            print('Wrote', model_path)

        # Export the ACE3P input file
        basepath, ext = os.path.splitext(args.template_filepath)
        op_path = '{}.py'.format(basepath)
        if not os.path.exists(op_path):
            print('Export operation not found at {}'.format(op_path))
        export_op = res_io.import_python_operation(op_path)

        params_att = export_op.parameters()
        params_att.findVoid('TestMode').setIsEnabled(True)
        params_att.findResource('attributes').setValue(att_resource)
        params_att.findResource('model').setValue(model_resource)
        params_att.findFile('MeshFile').setValue(args.model_filepath)
        params_att.findDirectory('OutputFolder').setValue(args.output_folder)

        # Use the yml file base as the output file prefix
        yml_filename = os.path.basename(args.yml_filepath)
        prefix, ext = os.path.splitext(yml_filename)
        params_att.findString('OutputFilePrefix').setValue(prefix)

        result = export_op.operate()
        outcome = result.findInt('outcome')
        OPERATION_SUCCEEDED = 3  # defined in smtk/operation/Operation.h
        assert outcome.value() == OPERATION_SUCCEEDED, \
            'export operation failed, outcome: {}'.format(outcome.value())

        # Check that expected file was created
        file_item = result.findFile('OutputFile')
        assert file_item is not None, 'Result missing OutputFile item'
        assert file_item.isSet(), 'Result OutputFile item not set'
        expected_path = file_item.value()
        print('output file:', expected_path)
        assert os.path.exists(expected_path), 'Output file not found at {}'.format(expected_path)

        # Dump out any warnings
        if export_op.log().numberOfRecords():
            print('EXPORT LOG RECORDS:')
            print(export_op.log().convertToString())

        # Check for baseline path to compare
        output_filename = os.path.basename(expected_path)
        yml_path = os.path.dirname(args.yml_filepath)
        baseline_filepath = os.path.join(yml_path, 'baseline', output_filename)
        if os.path.exists(baseline_filepath):
            print('Comparing output file with baseline')
            with open(baseline_filepath) as bf:
                baseline_lines = bf.readlines()
            with open(expected_path) as of:
                output_lines = of.readlines()

            import difflib
            diff = difflib.unified_diff(baseline_lines, output_lines, fromfile='baseline', tofile=output_filename, n=0)
            delta = [l for l in diff]
            if delta:
                print('Output file does not match baseline')
                print('Baseline file', baseline_filepath)
                print('Output file:', expected_path)
                sys.stderr.write('========== diff baseline output ==========\n')
                sys.stderr.writelines(delta)
                sys.stderr.write('==========================================\n')
                raise RuntimeError('Output file does NOT match baseline')
            else:
                print('Output file MATCHES baseline file')

    # finis
