using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace DataSetService
{

    public interface ITable
    {
        Dictionary<string, IColumn> Columns
        {
            get;
        }
        int X
        {
            get;
            set;
        }
        int Y
        {
            get;
            set;
        }

        DataTable Table
        {
            get;
            set;
        }

        string Name
        {
            get;
            set;
        }

        void Remove();

        IDataSetDesktop Desktop
        {
            get;
            set;
        }

    }

    public interface IColumn
    {
        ITable Table
        {
            get;
            set;
        }

        string Name
        {
            get;
            set;
        }
        DataColumn Column
        {
            get;
            set;
        }

        bool IsMarked
        {
            get;
            set;
        }

        string Type
        {
            get;
            set;
        }

        bool IsNullable
        {
            get;
            set;
        }

        bool IsNull
        {
            get;
            set;
        }

        string Modifier
        {
            get;
            set;
        }

        string Value
        {
            get;
            set;
        }

        int Length
        {
            get;
            set;
        }
    }


    public interface IDataSetDesktopFactory
    {
        IColumn Column
        {
            get;
        }

        ITable Table
        {
            get;
        }

        IDataSetDesktop Desktop
        {
            get;
        }

        ILink Link
        {
            get;
        }

        IDataSetDesktop Copy(IDataSetDesktop desktop);

        IDataSetDesktop Create(DataSet dataSet);

        void Set(IDataSetDesktop desktop, DataSet dataSet);

        /// <summary>
        /// Gets modifiers
        /// </summary>
        /// <param name="type">Type</param>
        /// <returns>Modifiers</returns>
        string[] GetModifiers(string type);
    }


    public class DataSetFactoryPerformer
    {

        static private string[][] mod = null;

        static private string[][] modf
        {
            get
            {
                if (mod != null)
                {
                    return mod;
                }
                mod = new string[3][];
                mod[0] = new string[]{ "=" };
                mod[1] = new string[]{ "=", ">", "<" };
                mod[2] = new string[] { "=", "LIKE" };
                return mod;
            }
        }
        public static ITable Copy(ITable table, IDataSetDesktopFactory factoty)
        {
            ITable t = factoty.Table;
            t.Name = table.Name;
            t.X = table.X;
            t.Y = table.Y;
            foreach (string name in table.Columns.Keys)
            {
                IColumn c = table.Columns[name];
                IColumn col = factoty.Column;
                t.Columns[name] = col;
                col.Column = c.Column;
                col.IsMarked = c.IsMarked;
                col.Table = t;
                col.Type = c.Type;
                col.IsNullable = c.IsNullable;
                col.IsNull = c.IsNull;
                col.Modifier = c.Modifier;
                col.Length = c.Length;
                col.Value = c.Value;
                col.Name = name;
            }
            return t;
        }

        public static IDataSetDesktop Copy(IDataSetDesktop desktop, IDataSetDesktopFactory factory)
        {
            IDataSetDesktop d = factory.Desktop;
            Dictionary<string, Dictionary<string, IColumn>> dictionary = new Dictionary<string, Dictionary<string, IColumn>>();
            foreach (string name in desktop.Tables.Keys)
            {
                ITable t = Copy(desktop.Tables[name], factory);
                d.Tables[name] = t;
                t.Desktop = d;
           }
           foreach (ITable t in d.Tables.Values)
           {
               Dictionary<string, IColumn> dic = new Dictionary<string, IColumn>();
               dictionary[t.Name] = dic;
               foreach (IColumn c in t.Columns.Values)
               {
                   dic[c.Name] = c;
               }
           }
           foreach (ILink link in desktop.Links)
           {
               ILink l = factory.Link;
               l.Desktop = d;
               l.IsMarked = link.IsMarked;
               l.SourceTable = link.SourceTable;
               l.TargetTable = link.TargetTable;
               l.SourceColumn = link.SourceColumn;
               l.TargetColumn = link.TargetColumn;
               l.Source = dictionary[l.SourceTable][l.SourceColumn];
               l.Target = dictionary[l.TargetTable][l.TargetColumn];
               if (!d.Links.Contains(l))
               {
                   d.Links.Add(l);
               }
           }

           return d;
        }

        public static ITable Create(DataTable dataTable, IDataSetDesktopFactory factory)
        {
            ITable table = factory.Table;
            table.Name = dataTable.TableName;
            table.Table = dataTable;
            foreach (DataColumn col in dataTable.Columns)
            {
                IColumn c = factory.Column;
                string name = col.ColumnName;
                c.Column = col;
                c.Table = table;
                c.Type = col.DataType + "";
                c.IsNullable = col.AllowDBNull;
                try
                {
                    c.Length = col.MaxLength;
                }
                catch (Exception)
                {
                }
                c.Name = name;
                table.Columns[name] = c;
            }
            return table;
        }

        public static IDataSetDesktop Create(DataSet dataSet, IDataSetDesktopFactory factory)
        {
            IDataSetDesktop d = factory.Desktop;
            foreach (DataTable table in dataSet.Tables)
            {
                ITable t = Create(table, factory);
                d.Tables[t.Name] = t;
                t.Desktop = d;
            }
            return d;
        }

        public static void Set(DataTable dataTable, ITable table)
        {
            table.Table = dataTable;
            table.Name = dataTable.TableName;
            foreach (DataColumn c in dataTable.Columns)
            {
                string name = c.ColumnName;
                table.Columns[name].Column = c;
                table.Columns[name].Type = c.DataType + "";
            }
        }

        public static void Set(DataSet dataSet, IDataSetDesktop desktop)
        {
            foreach (DataTable t in dataSet.Tables)
            {
                string name = t.TableName;
                ITable table = desktop.Tables[name];
            }
        }

        public static string[] GetModifiers(string type)
        {
            if (type.Equals("System.Char[]"))
            {
                return modf[2];
            }
            if (type.Equals("System.Double") | type.Equals("System.Int32"))
            {
                return modf[1];
            }
            return modf[0];
        }

    }

    public interface ILink
    {
        IColumn Source
        {
            get;
            set;
        }

        IColumn Target
        {
            get;
            set;
        }

        IDataSetDesktop Desktop
        {
            get;
            set;
        }

        string SourceTable
        {
            get;
            set;
        }

        string TargetTable
        {
            get;
            set;
        }

        string SourceColumn
        {
            get;
            set;
        }

        string TargetColumn
        {
            get;
            set;
        }

        bool IsMarked
        {
            get;
            set;
        }

        void Remove();
    }

    public interface IDataSetDesktop
    {
        Dictionary<string, ITable> Tables
        {
            get;
        }

        List<ILink> Links
        {
            get;
        }

        void Remove(ITable table);

        void Remove(ILink link);
    }

    public interface IQueryFactory
    {
        string GetQuery(IDataSetDesktop desktop);
    }

    public class AbstractTable : ITable
    {
        #region Fields
        internal static readonly AbstractTable Object = new AbstractTable();
        protected Dictionary<string, IColumn> columns = new Dictionary<string, IColumn>();
        protected int x;
        protected int y;
        protected DataTable table;
        protected string name;
        protected AbstractDataSetDesktop desktop;


        #endregion

        #region Constructors

        protected AbstractTable()
        {
        }

        #endregion

        #region ITable Members

        public Dictionary<string, IColumn> Columns
        {
            get
            {
                return columns;
            }
        }

        public int X
        {
            get
            {
                return x;
            }
            set
            {
                x = value;
            }
        }

        public int Y
        {
            get
            {
                return y;
            }
            set
            {
                y = value;
            }
        }

        public DataTable Table
        {
            get
            {
                return table;
            }
            set
            {
                table = value;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public IDataSetDesktop Desktop
        {
            get
            {
                return desktop;
            }
            set
            {
                desktop = value as AbstractDataSetDesktop;
            }
        }

        public void Remove()
        {
            AbstractDataSetDesktop.RemoveTable(this);
        }


        #endregion

    }

    public class AbstractColumn : IColumn
    {

        #region Fields

        static readonly internal AbstractColumn Object = new AbstractColumn();

        protected ITable table;
        protected DataColumn column;
        protected string name;
        protected bool isMarked = false;
        protected string type;

        protected bool isNullable = false;

        protected bool isNull = false;

        protected string modifier = "";

        protected string val = "";

        protected int length = 0;


        #endregion

        protected AbstractColumn()
        {
        }

        #region IColumn Members

        public ITable Table
        {
            get
            {
                return table;
            }
            set
            {
                table = value;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public DataColumn Column
        {
            get
            {
                return column;
            }
            set
            {
                column = value;
            }
        }

        public bool IsMarked
        {
            get
            {
                return isMarked;
            }
            set
            {
                isMarked = value;
            }
        }

        public string Type
        {
            get
            {
                return type;
            }
            set
            {
                type = value;
            }
        }

        public bool IsNullable
        {
            get { return isNullable; }
            set { isNullable = value; }
        }

        public bool IsNull
        {
            get { return isNull; }
            set { isNull = value; }
        }

        public string Modifier
        {
            get { return modifier; }
            set { modifier = value; }
        }

        public string Value
        {
            get { return val; }
            set { val = value; }
        }


        public virtual int Length
        {
            get
            {
                return length;
            }
            set
            {
                length = value;
            }
        }



        #endregion

        #region Specific Members

        static public List<ILink> GetLinks(IColumn column)
        {
            ITable t = column.Table;
            IDataSetDesktop d = t.Desktop;
            List<ILink> list = new List<ILink>();
            List<ILink> l = d.Links;
            foreach (ILink link in l)
            {
                if (link.Source == column | link.Target == column)
                {
                    list.Add(link);
                }
            }
            return list;
        }

        static public List<ILink> GetLinks(ITable table)
        {
            List<ILink> list = new List<ILink>();
            foreach (IColumn c in table.Columns.Values)
            {
                List<ILink> l = GetLinks(c);
                list.AddRange(l);
            }
            return list;

        }


        protected void setTable(ITable table)
        {
            this.table = table;
        }

        #endregion
    }

    public class AbstractLink : ILink
    {
        private AbstractColumn source;
        private AbstractColumn target;
        private AbstractDataSetDesktop desktop;
        protected string sourceTable;
        protected string targetTable;
        protected string sourceColumn;
        protected string targetColumn;
        protected bool isMarked;
        #region ILink Members


        public IColumn Source
        {
            get
            {
                return source;
            }
            set
            {
                source = value as AbstractColumn;
            }
        }

        public IColumn Target
        {
            get
            {
                return target;
            }
            set
            {
                Check(this, value);
                target = value as AbstractColumn;
            }
        }

        public IDataSetDesktop Desktop
        {
            get
            {
                return desktop;
            }
            set
            {
                desktop = value as AbstractDataSetDesktop;
            }
        }

        public string SourceTable
        {
            get
            {
                return sourceTable;
            }
            set
            {
                sourceTable = value;
            }
        }

        public string TargetTable
        {
            get
            {
                return targetTable;
            }
            set
            {
                targetTable = value;
            }
        }

        public string SourceColumn
        {
            get
            {
                return sourceColumn;
            }
            set
            {
                sourceColumn = value;
            }
        }

        public string TargetColumn
        {
            get
            {
                return targetColumn;
            }
            set
            {
                targetColumn = value;
            }
        }

        public bool IsMarked
        {
            get
            {
                return isMarked;
            }
            set
            {
                isMarked = value;
            }
        }


        public void Remove()
        {
            AbstractDataSetDesktop.RemoveLink(this);
        }



        #endregion

        #region Specific Members

        void set()
        {
            Set(this, sourceTable, targetTable, sourceColumn, targetColumn);
        }

        static public void Check(ILink link, IColumn target)
        {
            if (link.Source == target)
            {
                throw new Exception("Cannot link to itself");
            }
            IDataSetDesktop d = link.Source.Table.Desktop;
            List<ILink> links = d.Links;
            foreach (ILink l in links)
            {
                if (l.Source == link.Source && target == l.Target)
                {
                    throw new Exception("Link already exists");
                }
            }
            if (!link.Source.Type.Equals(target.Type))
            {
                throw new Exception("Different types of columns");
            }
        }

        static public void Set(ILink link, string sourceTable, string targetTable, string sourceColumn, string targetColumn)
        {
            IDataSetDesktop d = link.Desktop;
            ITable tt = d.Tables[targetTable];
            IColumn tc = tt.Columns[targetColumn];
            link.Target = tc;
            ITable st = d.Tables[sourceTable];
            IColumn sc = st.Columns[sourceColumn];
            link.Source = sc;
        }

        static public ILink Find(string tableName, string columnName, Dictionary<string, Dictionary<string, ILink>> dictionary)
        {
            return dictionary[tableName][columnName];
        }


 

        #endregion
    }

    public abstract class AbstractDataSetDesktop : IDataSetDesktop, IDataSetDesktopFactory
    {
        
        protected Dictionary<string, ITable> tables = new Dictionary<string, ITable>();
 
        protected List<ILink> links = new List<ILink>();
 
        #region IDataSetDesktop Members



        public Dictionary<string, ITable> Tables
        {
            get
            {
                return tables;
            }
        }

        public List<ILink> Links
        {
            get
            {
                return links;
            }
        }

        public void Remove(ITable table)
        {
            RemoveTable(table);
        }

        public void Remove(ILink link)
        {
            RemoveLink(link);
        }

        #endregion

        #region IDataSetDesktopFactory Members

        public abstract IColumn Column
        {
            get;
        }

        public abstract ITable Table
        {
            get;
        }

        public abstract ILink Link
        {
            get;
        }


        public abstract IDataSetDesktop Desktop
        {
            get;
        }

        public IDataSetDesktop Copy(IDataSetDesktop desktop)
        {
            return DataSetFactoryPerformer.Copy(desktop, this);
        }

        public IDataSetDesktop Create(DataSet dataSet)
        {
            return DataSetFactoryPerformer.Create(dataSet, this);
        }

        public void Set(IDataSetDesktop desktop, DataSet dataSet)
        {
            DataSetFactoryPerformer.Set(dataSet, desktop);
        }

        /// <summary>
        /// Gets modifiers
        /// </summary>
        /// <param name="type">Type</param>
        /// <returns>Modifiers</returns>
        public string[] GetModifiers(string type)
        {
            return DataSetFactoryPerformer.GetModifiers(type);
        }



        #endregion

        #region Specific Members

        static public void RemoveTable(ITable table)
        {
            IDataSetDesktop d = table.Desktop;
            List<ILink> l = new List<ILink>();
            List<ILink> lp = d.Links;
            foreach (ILink link in lp)
            {
                if (link.Source == table && link.Target == table)
                {
                    if (!l.Contains(link))
                    {
                        l.Add(link);
                    }
                }
            }
            foreach (ILink link in l)
            {
               d.Remove(link);
            }
            d.Remove(table);
        }

        static public void RemoveLink(ILink link)
        {
            IDataSetDesktop d = link.Desktop;
            d.Remove(link);
        }

        static public List<IColumn> GetSelectedColumns(IDataSetDesktop desktop)
        {
            List<IColumn> list = new List<IColumn>();
            foreach (ITable t in desktop.Tables.Values)
            {
                foreach (IColumn c in t.Columns.Values)
                {
                    if (c.IsMarked)
                    {
                        list.Add(c);
                    }
                }
            }
            return list;
        }

        static public List<ILink> GetSelectedLinks(IDataSetDesktop desktop)
        {
            List<ILink> list = new List<ILink>();
            foreach (ILink l in desktop.Links)
            {
                if (l.IsMarked)
                {
                    list.Add(l);
                }
            }
            return list;
        }

        static public List<IColumn[]> GetLinkColumns(IDataSetDesktop desktop)
        {
            List<ILink> list = GetSelectedLinks(desktop);
            List<IColumn[]> l = new List<IColumn[]>();
            foreach (ILink link in list)
            {
                IColumn[] c = new IColumn[2];
                c[0] = link.Source;
                c[1] = link.Target;
                l.Add(c);
            }
            return l;

        }

        static public string GenerateStandardStatement(IDataSetDesktop desktop)
        {
            List<IColumn> cols = GetSelectedColumns(desktop);
            List<IColumn[]> links = GetLinkColumns(desktop);
            List<ITable> tables = new List<ITable>();
            foreach (IColumn c in cols)
            {
                ITable t = c.Table;
                if (!tables.Contains(t))
                {
                    tables.Add(t);
                }
            }
            List<IColumn[]> linksn = new List<IColumn[]>(links);
            foreach (IColumn[] c in linksn)
            {
                if (!tables.Contains(c[0].Table) | !tables.Contains(c[1].Table))
                {
                    if (links.Contains(c))
                    {
                        links.Remove(c);
                    }
                }
            }
            string s = "SELECT ";
            bool bg = false;
            foreach (IColumn c in cols)
            {
                if (bg)
                {
                    s += ", ";
                }
                bg = true;
                s += c.Table.Name + "." + c.Name;
            }
            s += " FROM ";
            bg = false;
            foreach (ITable t in tables)
            {
                if (bg)
                {
                    s += ", ";
                }
                bg = true;
                s += t.Name;
             }
             string wh = "";
            bg = false;
            foreach (IColumn[] c in links)
            {
                if (bg)
                {
                    wh += " AND ";
                }
                bg = true;
                wh += c[0].Table.Name + "." + c[0].Name + " = " + c[1].Table.Name + "."  + c[1].Name;
            }
            foreach (IColumn c in cols)
            {
                if (c.IsNullable)
                {
                    if (!c.IsNull)
                    {
                        if (bg)
                        {
                            wh += " AND ";
                        }
                        bg = true;
                        wh += c.Table.Name + "." + c.Name + " IS NOT NULL";
                    }
                }
            }
            foreach (ITable table in desktop.Tables.Values)
            {
                foreach (IColumn c in table.Columns.Values)
                {
                    string mod = c.Modifier;
                    string val = c.Value;
                    if (mod.Length > 0 & val.Length > 0)
                    {
                        if (bg)
                        {
                            wh += " AND ";
                        }
                        wh += c.Table.Name + "." + c.Name + " " + mod + " ";
                        if (c.Type.Equals("System.Char[]"))
                        {
                            wh += "\'";
                        }
                        wh += val;
                        if (c.Type.Equals("System.Char[]"))
                        {
                            wh += "\'";
                        }
                        bg = true;
                    }
                }
            }
            if (wh.Length > 0)
            {
                s += " WHERE " + wh;
            }
            return s;
        }


        #endregion
    }
}
