﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace ProjectGen
{
    internal class Program
    {
        private const string RootDirectory = @"ProjectGen";
        private const string PlatformArgumentSpecifier = @"--platform=";
        private const string PackageConfigFile = "packages.config";
        private const string OutputProjectFileName = "stormrunner.csproj";

        private Program()
        {
        }

        internal static void Main(string[] args)
        {
            new Program().Run(args);
        }

        private void Run(IEnumerable<string> args)
        {
            // TODO: make this better. It was written as a brute-force approach.
            var platformToGenerate = Environment.OSVersion.Platform;

            var platformArgument = args.FirstOrDefault(a => a.StartsWith(PlatformArgumentSpecifier, StringComparison.InvariantCulture));
            if (platformArgument != null)
            {
                var platformString = platformArgument.Substring(PlatformArgumentSpecifier.Length);

                PlatformID parsed;
                if (Enum.TryParse(platformString, out parsed))
                {
                    platformToGenerate = parsed;
                }
            }

            var platformName = platformToGenerate.ToString().ToLowerInvariant();
            Console.WriteLine("Attempting to generate for platform '{0}'.", platformName);

            var configFile = string.Format(@"{0}{2}{1}.config", RootDirectory, platformName, Path.DirectorySeparatorChar);
            if (!File.Exists(configFile))
            {
                Console.WriteLine("Couldn't find the configuration file {0}.", configFile);
                return;
            }

            var platformConfiguration = new Platform();
            platformConfiguration.LoadFromFile(configFile);

            var template = new StringBuilder(File.ReadAllText(string.Format("{0}{2}{1}.template", RootDirectory, platformName, Path.DirectorySeparatorChar)));
            var projectConstants = platformConfiguration.DefinedConstants != null ? string.Join(";", platformConfiguration.DefinedConstants) : string.Empty;
            template = template.Replace("{DEFINED_CONSTANTS}", projectConstants);

            var referencesBuilder = new StringBuilder();
            foreach (var reference in platformConfiguration.ReferencesAndHints)
            {
                var referenceRoot = new XElement("Reference", new XAttribute("Include", reference.Key));
                if (!string.IsNullOrEmpty(reference.Value))
                {
                    referenceRoot.Add(new XElement("HintPath", reference.Value));
                }

                referencesBuilder.AppendLine(referenceRoot.ToString());
            }

            template = template.Replace("{REFERENCES}", referencesBuilder.ToString().Trim());

            var filesOutput = GenerateProjectFilesXml(platformConfiguration);
            template = template.Replace("{PROJECT_FILES}", filesOutput.Trim());

            if (File.Exists(OutputProjectFileName))
            {
                File.Move(OutputProjectFileName, string.Format("{0}.{1}.backup", OutputProjectFileName, DateTime.Now.ToString("yyyyMMdd_hhmmss")));
            }

            File.WriteAllText(OutputProjectFileName, template.ToString());

            if (!File.Exists(PackageConfigFile))
            {
                var templatePackageConfigFile = string.Format(@"{0}{2}packages.{1}.config", RootDirectory, platformName, Path.DirectorySeparatorChar);
                if (File.Exists(templatePackageConfigFile))
                {
                    File.Copy(templatePackageConfigFile, PackageConfigFile);
                }
                else
                {
                    Console.WriteLine("Unable to copy package config file {0} to {1}.", templatePackageConfigFile, PackageConfigFile);
                }
            }
        }

        private string GenerateProjectFilesXml(Platform platform)
        {
            var output = new StringBuilder();

            var filesListFile = string.Format(@"{0}{1}solutionFiles.xml", RootDirectory, Path.DirectorySeparatorChar);
            XDocument document = XDocument.Load(filesListFile);
            if (document.Root == null)
            {
                Console.WriteLine("Cannot load the list of files to put in the project!");
                return string.Empty;
            }

            var fileElements = document.Root.Descendants().Where(IsValidFileListElement);
            foreach (var element in fileElements)
            {
                var fileNameAttribute = element.Attributes().FirstOrDefault(e => e.Name == "Include");
                if (fileNameAttribute == null || string.IsNullOrEmpty(fileNameAttribute.Value))
                {
                    continue;
                }

                var isContent = false;
                var fileBuildAction = element.Name.ToString();
                if (fileBuildAction.Equals("Resource"))
                {
                    fileBuildAction = platform.ContentElementName;
                    isContent = true;
                }

                var contentElement = new XElement(fileBuildAction, new XAttribute("Include", fileNameAttribute.Value));

                if (isContent && platform.ContentElementShouldCopy)
                {
                    var copyElement = new XElement("CopyToOutputDirectory", "PreserveNewest");
                    contentElement.Add(copyElement);
                }

                output.AppendLine(contentElement.ToString());
            }

            return output.ToString();
        }

        private bool IsValidFileListElement(XElement element)
        {
            return element.FirstAttribute != null &&
                   element.FirstAttribute.Name == "Include" &&
                   (element.Name == "Compile" || element.Name == "None" || element.Name == "Resource");
        }
    }
}
