using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.Client;
using System.Collections.Specialized;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.Xml;
using System.IO;
using Microsoft.Build.BuildEngine;
using Microsoft.TeamFoundation.Build.Proxy;

namespace TFSWrappers
{
    public enum BuildStatus
    {
        InProgress,
        NotRunning,
        Unknown
    }

    public class TFSBuildType : IDisposable
    {
        public TFSBuildType(SourceControlWrapper sourceControlWrapper, WorkspaceWrapper workspaceWrapper, BuildStoreWrapper buildStoreWrapper, 
            string projectName, string buildTypeName)
        {
            this.sourceControlWrapper = sourceControlWrapper;
            this.workspaceWrapper = workspaceWrapper;
            this.needToCreateWorkspace = false;
            this.buildStoreWrapper = buildStoreWrapper;
            this.serverName = buildStoreWrapper.TFS.Name;
            this.projectName = projectName;
            this.name = buildTypeName;
            InitBuildType();
        }

        public TFSBuildType(BuildStoreWrapper buildStoreWrapper, string projectName, string buildTypeName)
        {
            this.needToCreateWorkspace = true;
            this.buildStoreWrapper = buildStoreWrapper;
            this.serverName = buildStoreWrapper.TFS.Name;
            this.projectName = projectName;
            this.name = buildTypeName;
            InitBuildType();
        }

        public TFSBuildType(string serverName , string projectName, string buildTypeName)
        {
            this.needToCreateWorkspace = true;
            buildStoreWrapper = new BuildStoreWrapper(serverName);
            this.serverName = serverName;
            this.projectName = projectName;
            this.name = buildTypeName;
            InitBuildType();
        }

        public TFSBuildType(ConfigurationType configurationType, string buildTypeName)
        {
            this.needToCreateWorkspace = true;
            NameValueCollection appSettings = TFSFunctions.GetTFSAppSettingsSection(configurationType);
            this.serverName = appSettings[TFSConstants.ServerAppSettingsValueName];
            this.projectName = appSettings[TFSConstants.ProjectAppSettingsValueName];
            this.name = buildTypeName;
            buildStoreWrapper = new BuildStoreWrapper(serverName);
            InitBuildType();
        }

        private void InitBuildType()
        {
            if (Name != Constants.AllBuildTypes)
            {
                if (needToCreateWorkspace)
                {
                    sourceControlWrapper = new SourceControlWrapper(buildStoreWrapper.TFS);
                }
                fileServerPath = "$/" + projectName + "/TeamBuildTypes/" + name + "/TFSBuild.proj";
                fileLocalPath = Path.GetTempFileName();
                if (needToCreateWorkspace)
                {
                    workspaceWrapper = new WorkspaceWrapper(buildStoreWrapper.TFS, sourceControlWrapper.CreateTempWorkspace().Name);
                }
                workspaceWrapper.GetLatestVersion(fileServerPath, fileLocalPath);
                NameTable nameTable = new NameTable();
                xmlDocument = new XmlDocument(nameTable);
                xmlDocument.Load(fileLocalPath);
                xmlNamespaceManager = new XmlNamespaceManager(nameTable);
                xmlNamespaceManager.AddNamespace(Constants.NamespacePrefix, Constants.NamespaceUri);
                LoadBuildTypeProperties();
                buildsData = buildStoreWrapper.BS.GetListOfBuilds(ProjectName, Name);
            }
            else
            {
                buildMachine = new BuildTypeProperty();
                description = new BuildTypeProperty();
                buildDirectoryPath = new BuildTypeProperty();
                dropLocation = new BuildTypeProperty();
                runTest = new BuildTypeProperty();
                runCodeAnalysis = new BuildTypeProperty();
                updateAssociatedWorkItems = new BuildTypeProperty();
                workItemTitle = new BuildTypeProperty();
                descriptionText = new BuildTypeProperty();
                buildlogText = new BuildTypeProperty();
                errorWarningLogText = new BuildTypeProperty();
                workItemFieldValues = new BuildTypeProperty();
                buildsData = buildStoreWrapper.BS.GetListOfBuilds(ProjectName, String.Empty);
            }
        }

        private void LoadBuildTypeProperties()
        {
            buildMachine = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.BuildMachineXPath,
                "", "Build Machine", "Name of the machine which will be used to build the solutions selected.");
            description = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.DescriptionXPath,
                "", "Description", "The description is associated with a build type. Edit the value for making changes.");
            buildDirectoryPath = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.BuildDirectoryPathXPath,
                "", "Build Directory", "The directory on the build machine that will be used to build the selected solutions. The directory must be a local path on the build machine (e.g. c:\build).");
            dropLocation = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.DropLocationXPath,
                "", "Drop Location", "The location to drop (copy) the built binaries and the log files after the build is complete. This location has to be a valid UNC path of the form \\\\Server\\Share. The build machine service account and application tier account need to have read write permission on this share.");
            runTest = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.RunTestXPath,
                "false", "Run Test", "Set this flag to enable/disable running tests as a post build step.");
            runCodeAnalysis = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.RunCodeAnalysisXPath,
                "Never", "Run Code Analysis", "To change CodeAnalysis behavior edit this value. Valid values for this can be Default,Always or Never.");
            updateAssociatedWorkItems = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.UpdateAssociatedWorkItemsXPath,
                Boolean.TrueString, "Update Associated Work Items", "Set this flag to enable/disable updating associated workitems on a successful build.");
            workItemTitle = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.WorkItemTitleXPath,
                "Build failure in build:", "Work Item Title", "Title for the work item created on build failure.");
            descriptionText = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.DescriptionTextXPath,
                "This work item was created by Team Build on a build failure.", "Work Item Description", "Description for the work item created on build failure.");
            buildlogText = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.BuildlogTextXPath,
                "The build log file is at:", "Build Log Text", "Text pointing to log file location on build failure.");
            errorWarningLogText = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.ErrorWarningLogTextXPath,
                "The errors/warnings log file is at:", "Error Warning Log Text", "Text pointing to error/warnings file location on build failure.");
            workItemFieldValues = new BuildTypeProperty(xmlDocument, xmlNamespaceManager, Constants.WorkItemFieldValuesXPath,
                "Symptom=build break;Steps To Reproduce=Start the build using Team Build", "Work Item Field Values", "Add/edit key value pairs to set values for fields in the work item created during the build process. Please make sure the field names are valid for the work item type being used.");

            //Advanced attributes
            advancedProperties = new AdvancedBuildTypeProperties(xmlDocument, xmlNamespaceManager);
        }

        private SourceControlWrapper sourceControlWrapper; 
        private WorkspaceWrapper workspaceWrapper;
        private BuildStoreWrapper buildStoreWrapper;
        private XmlDocument xmlDocument;
        private XmlNamespaceManager xmlNamespaceManager;
        private bool inEditMode;
        private bool needToCreateWorkspace;

        private string fileServerPath;

        public string FileServerPath
        {
            get { return fileServerPath; }
        }

        private string fileLocalPath;

        public string FileLocalPath
        {
            get { return fileLocalPath; }
        }

        private string serverName;

        public string ServerName
        {
            get { return serverName; }
        }

        private string projectName;

        public string ProjectName
        {
            get { return projectName; }
        }

        private string name;

        public string Name
        {
            get { return name; }
        }

        public string SolutionRoot
        {
            get
            {
                string tempPath = fileServerPath.Substring(0, fileServerPath.LastIndexOf('/'));
                tempPath = tempPath.Substring(0, tempPath.LastIndexOf('/'));
                tempPath = tempPath.Substring(0, tempPath.LastIndexOf('/'));
                return tempPath;
            }
        }

        private BuildTypeProperty description;

        public BuildTypeProperty Description
        {
            get { return description; }
            set { description = value; }
        }

        private BuildTypeProperty buildMachine;

        public BuildTypeProperty BuildMachine
        {
            get { return buildMachine; }
            set { buildMachine = value; }
        }

        private BuildTypeProperty buildDirectoryPath;

        public BuildTypeProperty BuildDirectoryPath
        {
            get { return buildDirectoryPath; }
            set { buildDirectoryPath = value; }
        }

        private BuildTypeProperty dropLocation;

        public BuildTypeProperty DropLocation
        {
            get { return dropLocation; }
            set { dropLocation = value; }
        }

        private BuildTypeProperty runTest;

        public BuildTypeProperty RunTest
        {
            get { return runTest; }
            set { runTest = value; }
        }

        private BuildTypeProperty workItemFieldValues;

        public BuildTypeProperty WorkItemFieldValues
        {
            get { return workItemFieldValues; }
            set { workItemFieldValues = value; }
        }

        private BuildTypeProperty runCodeAnalysis;

        public BuildTypeProperty RunCodeAnalysis
        {
            get { return runCodeAnalysis; }
            set { runCodeAnalysis = value; }
        }

        private BuildTypeProperty updateAssociatedWorkItems;

        public BuildTypeProperty UpdateAssociatedWorkItems
        {
            get { return updateAssociatedWorkItems; }
            set { updateAssociatedWorkItems = value; }
        }

        private BuildTypeProperty workItemTitle;

        public BuildTypeProperty WorkItemTitle
        {
            get { return workItemTitle; }
            set { workItemTitle = value; }
        }

        private BuildTypeProperty descriptionText;

        public BuildTypeProperty DescriptionText
        {
            get { return descriptionText; }
            set { descriptionText = value; }
        }

        private BuildTypeProperty buildlogText;

        public BuildTypeProperty BuildlogText
        {
            get { return buildlogText; }
            set { buildlogText = value; }
        }

        private BuildTypeProperty errorWarningLogText;

        public BuildTypeProperty ErrorWarningLogText
        {
            get { return errorWarningLogText; }
            set { errorWarningLogText = value; }
        }

        private List<string> solutionsToBuild;

        public List<string> SolutionsToBuild
        {
            get { return solutionsToBuild; }
            set { solutionsToBuild = value; }
        }

        private List<string> configurationsToBuild;

        public List<string> ConfigurationsToBuild
        {
            get { return configurationsToBuild; }
            set { configurationsToBuild = value; }
        }

        private BuildTypeProperty metaDataFile;

        public BuildTypeProperty MetaDataFile
        {
            get { return metaDataFile; }
            set { metaDataFile = value; }
        }

        private BuildTypePropertyAttribute metaDataFileInclude;

        public BuildTypePropertyAttribute MetaDataFileInclude
        {
            get { return metaDataFileInclude; }
            set { metaDataFileInclude = value; }
        }

        private BuildTypeProperty testList;

        public BuildTypeProperty TestList
        {
            get { return testList; }
            set { testList = value; }
        }

        private AdvancedBuildTypeProperties advancedProperties;

        public AdvancedBuildTypeProperties AdvancedProperties
        {
            get { return advancedProperties; }
            set { advancedProperties = value; }
        }

        private BuildData[] buildsData;

        public BuildData[] BuildsData
        {
            get { return buildsData; }
        }

        private bool resumable;

	    public bool Resumable
	    {
		    get 
            { 
                return resumable;
            }
		    set 
            {
                ChangeResumableState(value);
                resumable = value;
            }
	    }
	
        public string Execute()
        {
            return Execute(BuildMachine.PropertyValue, DropLocation.PropertyValue);
        }

        public string Execute(string buildMachine, string dropLocation)
        {
            if (Name != Constants.AllBuildTypes)
            {
                BuildParameters buildParameters = new BuildParameters();
                buildParameters.TeamFoundationServer = buildStoreWrapper.TFS.Uri.AbsoluteUri;
                buildParameters.TeamProject = ProjectName;
                buildParameters.BuildType = Name;
                buildParameters.BuildMachine = buildMachine;
                buildParameters.BuildDirectory = BuildDirectoryPath.PropertyValue;
                buildParameters.DropLocation = dropLocation;
                BuildController buildController = (BuildController)buildStoreWrapper.TFS.GetService(typeof(BuildController));
                return buildController.StartBuild(buildParameters);
            }
            else
            {
                throw new ApplicationException("Cannot execute. Build type not selected.");
            }
        }

        public BuildStatus GetBuildStatus()
        {
            if (Name != Constants.AllBuildTypes)
            {
                try
                {
                    string uri = string.Format("tcp://{0}:{1}/{2}", BuildMachine.PropertyValue, "9191", typeof(Microsoft.TeamFoundation.Build.Common.IBuildAgent).FullName);
                    Microsoft.TeamFoundation.Build.Common.IBuildAgent agent = (Microsoft.TeamFoundation.Build.Common.IBuildAgent)Activator.GetObject(
                        typeof(Microsoft.TeamFoundation.Build.Common.IBuildAgent), uri);
                    if (agent.IsBuildInProgress(projectName))
                    {
                        return BuildStatus.InProgress;
                    }
                    else
                    {
                        return BuildStatus.NotRunning;
                    }
                }
                catch
                {
                    return BuildStatus.Unknown;
                }
            }
            else
            {
                return BuildStatus.Unknown;
            }
        }

        private void ChangeResumableState(bool resumable)
        {
            if (Name != Constants.AllBuildTypes)
            {
                if (!inEditMode)
                {
                    StartEdit();
                }
                advancedProperties.SkipClean.PropertyValue = resumable.ToString();
                advancedProperties.SkipGet.PropertyValue = resumable.ToString();
                advancedProperties.SkipInitializeWorkspace.PropertyValue = resumable.ToString();
                Save();
            }
        }

        public void StartEdit()
        {
            workspaceWrapper.CheckOutFile(fileServerPath, fileLocalPath, true, true);
            inEditMode = true;
        }

        public void CancelEdit()
        {
            xmlDocument.Load(fileLocalPath);
            LoadBuildTypeProperties();
            workspaceWrapper.UndoCheckOutFile(fileServerPath);
            inEditMode = false;
        }

        public void Save()
        {
            xmlDocument.Save(fileLocalPath);
            workspaceWrapper.CheckInFile(fileServerPath, "Edited by TFSBuildManager");
            inEditMode = false;
        }

        public void Dispose()
        {
            if ((Name != Constants.AllBuildTypes) && (needToCreateWorkspace))
            {
                workspaceWrapper.ClearFileMapping(fileServerPath);
                sourceControlWrapper.DeleteWorkspace(workspaceWrapper.WS.Name);
            }
        }
    }
}
