﻿using System;
using System.Collections.Generic;
using Oni.Collections;

namespace Oni
{
    internal sealed class DatPacker
    {
        private readonly List<string> inputPaths = new List<string>();
        private string targetFilePath;
        private bool targetBigEndian;
        private long targetTemplateChecksum;

        public List<string> InputPaths => inputPaths;

        public string TargetFilePath
        {
            get { return targetFilePath; }
            set { targetFilePath = value; }
        }

        public bool TargetBigEndian
        {
            get { return targetBigEndian; }
            set { targetBigEndian = value; }
        }

        public long TargetTemplateChecksum
        {
            get { return targetTemplateChecksum; }
            set { targetTemplateChecksum = value; }
        }

        public void Pack(InstanceFileManager fileManager, IEnumerable<string> filePaths)
        {
            var inputs = new List<InstanceFile>();
            var seenFilePaths = new Set<string>(StringComparer.OrdinalIgnoreCase);

            foreach (string filePath in filePaths)
            {
                if (seenFilePaths.Add(filePath))
                    inputs.Add(fileManager.OpenFile(filePath));
            }

            inputs.Reverse();

            var descriptors = GetImportedDescriptors(inputs);

            if (descriptors.Count > 0)
            {
                var writer = InstanceFileWriter.CreateV31(targetTemplateChecksum, targetBigEndian);
                writer.AddDescriptors(descriptors, true);

                Console.WriteLine("Writing {0}", targetFilePath);

                writer.Write(targetFilePath);
            }
        }

        public void Import(InstanceFileManager fileManager, string[] inputDirPaths)
        {
            Console.WriteLine("Reading files from {0}", string.Join(";", inputDirPaths));

            var inputsFiles = fileManager.OpenDirectories(inputDirPaths);
            var descriptors = GetImportedDescriptors(inputsFiles);

            if (descriptors.Count > 0)
            {
                var writer = InstanceFileWriter.CreateV31(targetTemplateChecksum, targetBigEndian);
                writer.AddDescriptors(descriptors, true);

                Console.WriteLine("Writing {0}", targetFilePath);

                writer.Write(targetFilePath);
            }
        }

        private static List<InstanceDescriptor> GetImportedDescriptors(List<InstanceFile> inputFiles)
        {
            var namedDescriptors = new Set<string>(StringComparer.Ordinal);
            var ignored = new Set<InstanceDescriptor>();

            foreach (var file in inputFiles)
            {
                foreach (var descriptor in file.GetNamedDescriptors())
                {
                    if (namedDescriptors.Contains(descriptor.FullName))
                    {
                        //Console.Error.WriteLine("WARNING: More than one instance has name {0}, ignoring.", descriptor.FullName);
                        ignored.Add(descriptor);
                    }
                    else
                    {
                        namedDescriptors.Add(descriptor.FullName);
                    }
                }
            }

            inputFiles.Sort((x, y) => string.Compare(x.Descriptors[0].FullName, y.Descriptors[0].FullName, StringComparison.Ordinal));

            var descriptors = new List<InstanceDescriptor>(4096);

            foreach (var file in inputFiles)
            {
                foreach (var descriptor in file.Descriptors)
                {
                    if (ignored.Contains(descriptor))
                        continue;

                    if (descriptor.HasName)
                    {
                        if (descriptor.IsPlaceholder && namedDescriptors.Contains(descriptor.FullName))
                            continue;

                        namedDescriptors.Add(descriptor.FullName);
                    }

                    descriptors.Add(descriptor);
                }
            }

            descriptors.Sort((x, y) => x.Template.IsLeaf.CompareTo(y.Template.IsLeaf));

            return descriptors;
        }
    }
}
