using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;
using System.Xml;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Drawing;
using System.IO;

namespace UnitTests {
    public class TestBase {

        public class NodeInfo {
            XmlNodeType nt;
            string name;
            string value;
            public NodeInfo(XmlReader r) {
                this.nt = r.NodeType;
                this.name = r.Name;
                this.value = Normalize(r.Value);
            }
            public bool Equals(NodeInfo other) {
                return this.nt == other.nt && this.name == other.name &&
                    this.value == other.value;
            }

            public XmlNodeType NodeType { get { return nt; } }

            string Normalize(string value) {
                // So text indented different still compares as the same.
                if (string.IsNullOrEmpty(value)) return null;
                StringBuilder sb = new StringBuilder();
                bool wasnewline = true; // collapse leading spaces
                for (int i = 0, n = value.Length; i < n; i++) {
                    char ch = value[i];
                    if (ch == '\r'){
                        if (i + 1 < n && value[i + 1] == '\n') {
                            i++;
                        }
                        sb.Append('\n');
                        wasnewline = true;
                    } else if (ch == '\n'){
                        sb.Append(ch);
                        wasnewline = true;
                    } else if (Char.IsWhiteSpace(ch)) {
                        if (!wasnewline) sb.Append(' ');
                    } else {
                        sb.Append(ch);
                        wasnewline = false;
                    }
                }
                return sb.ToString();
            }
        }        

        public void Sleep(int ms) {
            Thread.Sleep(ms);
        }

        public Window LaunchApp(string exeFileName, string args, string rootElementName) {
            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = exeFileName;
            info.Arguments = args;

            Process p = new Process();
            p.StartInfo = info;
            if (!p.Start()) {
                string msg = "Error launching " + exeFileName;
                MessageBox.Show("Error Creating Process", msg, 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                throw new Exception(msg);
            }
            if (p.HasExited) {
                throw new Exception(string.Format("Failed to launch '{0'}, exit code {1}", exeFileName, p.ExitCode.ToString()));
            }
            Window w = new Window(p, null, rootElementName);
            w.TestBase = this;
            return w;
        }

        protected Window window;

        public void CloseApp() {
            if (this.window != null) {
                this.window.Dispose();
                this.window = null;
            }
        }


        public void DeleteFile(string fname) {
            if (File.Exists(fname))
                File.Delete(fname);
        }

        public void CompareResults(List<NodeInfo> nodes, string outFile) {
            int pos = 0;
            XmlReader reader = XmlReader.Create(outFile);
            IXmlLineInfo li = (IXmlLineInfo)reader;
            XmlNodeType previousNodeType = XmlNodeType.None;
            using (reader) {
                while (reader.Read()) {
                    if (reader.NodeType == XmlNodeType.Whitespace ||
                        reader.NodeType == XmlNodeType.SignificantWhitespace ||
                        reader.NodeType == XmlNodeType.XmlDeclaration)
                        continue;

                    NodeInfo node = new NodeInfo(reader);
                    if (pos >= nodes.Count) {
                        throw new ApplicationException("Found too many nodes");
                    }
                    NodeInfo other = nodes[pos++];
                    if (!node.Equals(other)) {
                        throw new ApplicationException(
                                string.Format("Mismatching nodes at line {0},{1}",
                                li.LineNumber, li.LinePosition));
                    }
                    previousNodeType = node.NodeType;
                }
            }
        }

        public List<NodeInfo> ReadNodes(string fileName) {
            XmlReader reader = XmlReader.Create(fileName);
            return ReadNodes(reader);
        }

        public List<NodeInfo> ReadNodes(XmlReader reader) {        
            List<NodeInfo> nodes = new List<NodeInfo>();
            using (reader) {
                while (reader.Read()) {
                    if (reader.NodeType == XmlNodeType.Whitespace ||
                        reader.NodeType == XmlNodeType.SignificantWhitespace ||
                        reader.NodeType == XmlNodeType.XmlDeclaration)
                        continue;

                    nodes.Add(new NodeInfo(reader));
                }
            }
            return nodes;
        }

        public virtual void CheckClipboard(string expected) {
            if (!Clipboard.ContainsText()) {
                throw new ApplicationException("clipboard does not contain any text!");
            }
            string text = Clipboard.GetText();
            if (text != expected) {
                throw new ApplicationException("clipboard does not match expected cut node");
            }
        }

        public Window LaunchIE(string args) {
            // when IE launches it creates a new process instead of the process we create, so we have to track that jump.
            // so this means watching for a new process to appear.
            HashSet<int> runningProcesses = new HashSet<int>();
            foreach (Process e in Process.GetProcesses())
            {
                Debug.WriteLine("Found Process " + e.Id +  " : " + e.ProcessName);
                if (e.ProcessName == "iexplore")
                {
                    try {
                        e.Kill();
                    } catch { }
                }
                else
                {
                    runningProcesses.Add(e.Id);
                }
            }

            ProcessStartInfo info = new ProcessStartInfo();
            info.FileName = Environment.GetEnvironmentVariable("ProgramFiles") + "\\Internet Explorer\\iexplore.exe";;
            info.Arguments = args;

            Process p = new Process();
            p.StartInfo = info;
            if (!p.Start())
            {
                string msg = "Error launching " + info.FileName;
                MessageBox.Show("Error Creating Process", msg,
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                throw new Exception(msg);
            }
           
            // find the new process that has a window whose ClassName is "IEFrame".
            Process ie = null;
            int retry = 5;
            while (retry-- > 0)
            {
                foreach (Process np in Process.GetProcesses())
                {
                    if (!runningProcesses.Contains(np.Id))
                    {
                        Debug.WriteLine("Checking Process " + np.Id + " : " + np.ProcessName);
                        AutomationWrapper wrapper = Window.FindWindowForProcessId(np.Id, "IEFrame", null);
                        if (wrapper != null)
                        {
                            // found it!
                            ie = np;
                            p = np;
                            break;
                        }
                    }
                }
                if (ie != null)
                {
                    break;
                }
                Sleep(500);
            }

            if (ie != null)
            {
                Window w = new Window(p,  "IEFrame", null);
                w.TestBase = this;
                return w;
            }
            throw new Exception("Not finding the new IE process, perhaps you need to shutdown existing IE instances");
        }



    }

    
}