﻿using System;
using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Cosmos.Build.Tasks
{
    public class Ld : ToolTask
    {
        #region Task Parameters

        [Required]
        public ITaskItem[] InputFiles { get; set; }

        [Required]
        public string OutputFile { get; set; }

        public string Entry { get; set; }

        public string TextAddress { get; set; }

        public string DataAddress { get; set; }

        public string BssAddress { get; set; }

        #endregion

        protected override string ToolName => "ld.exe";

        protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High;
        protected override MessageImportance StandardOutputLoggingImportance => MessageImportance.High;

        private static bool IsValidAddress(string aAddress)
        {
            if (UInt64.TryParse(aAddress, out var xAddress))
            {
                return true;
            }

            if (aAddress.StartsWith("0x")
                && UInt64.TryParse(aAddress.Remove(0, 2), NumberStyles.AllowHexSpecifier, null, out xAddress))
            {
                return true;
            }

            return false;
        }

        protected override bool ValidateParameters()
        {
            if (InputFiles.Length == 0)
            {
                Log.LogError("No input files specified!");
            }


            foreach (var xFile in InputFiles)
            {
                var xFullPath = xFile.GetMetadata("FullPath");

                if (String.IsNullOrWhiteSpace(xFullPath))
                {
                    Log.LogError($"Input file is an empty string! Input files: '{String.Join(";", InputFiles.Select(f => f.GetMetadata("Identity")))}'");
                }
                else if (!File.Exists(xFullPath))
                {
                    Log.LogError($"Input file '{xFullPath}' doesn't exist!");
                }
            }

            if (String.IsNullOrEmpty(OutputFile))
            {
                Log.LogError("No output file specified!");
            }

            if (String.IsNullOrWhiteSpace(Entry))
            {
                Entry = null;
            }

            if (String.IsNullOrWhiteSpace(TextAddress))
            {
                TextAddress = null;
            }
            else if (!IsValidAddress(TextAddress))
            {
                Log.LogError(nameof(TextAddress) + " isn't a valid 64-bit number!");
            }

            if (String.IsNullOrWhiteSpace(DataAddress))
            {
                DataAddress = null;
            }
            else if (!IsValidAddress(DataAddress))
            {
                Log.LogError(nameof(DataAddress) + " isn't a valid 64-bit number!");
            }

            if (String.IsNullOrWhiteSpace(BssAddress))
            {
                BssAddress = null;
            }
            else if (!IsValidAddress(BssAddress))
            {
                Log.LogError(nameof(BssAddress) + " isn't a valid 64-bit number!");
            }

            return !Log.HasLoggedErrors;
        }

        protected override string GenerateFullPathToTool()
        {
            if (String.IsNullOrWhiteSpace(ToolExe))
            {
                return null;
            }

            if (String.IsNullOrWhiteSpace(ToolPath))
            {
                return Path.Combine(Directory.GetCurrentDirectory(), ToolExe);
            }

            return Path.Combine(Path.GetFullPath(ToolPath), ToolExe);
        }

        protected override string GenerateCommandLineCommands()
        {
            var xBuilder = new CommandLineBuilder();

            xBuilder.AppendSwitchIfNotNull("-Ttext ", TextAddress);
            xBuilder.AppendSwitchIfNotNull("-Tdata ", DataAddress);
            xBuilder.AppendSwitchIfNotNull("-Tbss ", BssAddress);
            xBuilder.AppendSwitchIfNotNull("-e ", Entry);
            xBuilder.AppendSwitchIfNotNull("-o ", OutputFile);

            xBuilder.AppendFileNamesIfNotNull(InputFiles, " ");

            Log.LogMessage(MessageImportance.High, xBuilder.ToString());

            return xBuilder.ToString();
        }
    }
}
