using System;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using Gtk;
using Glade;
using Pango;
using Mono.Unix;
using Orienteering.Standard;

using OrComp = Orienteering.Competition;

namespace Orienteering.GUI {
    public class MainApp {
        public static void Main (string[] args) {
            Catalog.Init("Main", "./locale");
            new MainApp(args);
        }

        private Competition comp;
        private DataBase dataBase;
        string opened_file;

        private double ChangeAdjustment(Adjustment a, double pom) {
            double x = a.Value + a.PageSize/2;
            return x/pom - a.PageSize/2;
        }

        private bool zmena_expose;
        private void Zmena (double pom) {
            if (comp != null) if (comp.DrawStage!=null) {
                zmena_expose = false;
                drawingarea1.SetSizeRequest 
                    ((int)Math.Ceiling(comp.DrawStage.map.Width*Config.Zoom),
                     (int)Math.Ceiling(comp.DrawStage.map.Height*Config.Zoom) );
                scrolledwindow1.Hadjustment.Value =
                    ChangeAdjustment(scrolledwindow1.Hadjustment, pom);
                scrolledwindow1.Vadjustment.Value =
                    ChangeAdjustment(scrolledwindow1.Vadjustment, pom);

                zmena_expose = true;
                OnExposed(null, null);
                entry1.Text = Math.Round(100*Config.Zoom).ToString()+"%";
            }
        }

        public MainApp (string[] args) {
            Application.Init();

            Glade.XML gxml = new Glade.XML (null, "bakalar.glade", "window1", null);
            gxml.Autoconnect (this);

            Config.Dpi = 150;

            drawingarea1.ExposeEvent += OnExposed;
            scrolledwindow1.KeyPressEvent += KeyPressHandler;
            scrolledwindow1.ButtonPressEvent += ButtonPressHandler;

            if (args.Length>0) {
                try {
                    dataBase = new DataBase();
                    opened_file = args[0];
                    comp = Orienteering.GUI.Competition.Open(args[0], dataBase);
                    comp.DrawStage = comp.Stage(1, dataBase);
                }
                catch (Exception e) { 
                    Orienteering.GUI.Standard.Error("Zlyhalo otvorenie suboru");
                    Console.WriteLine(e.ToString());
                }
            }

            window1.Resize(500, 500);
            window1.ShowAll();
            Application.Run();
        }

        [Widget] DrawingArea drawingarea1;
        [Widget] internal Window window1;
        [Widget] ScrolledWindow scrolledwindow1;
        [Widget] Entry entry1;

        private void OnWindowDeleteEvent (object o, DeleteEventArgs args) {
            if (dataBase!=null)
                dataBase.Close();
            Application.Quit();
            args.RetVal = true;
        }

        private void OnQuitActivated(object o, EventArgs args) {
            if (dataBase!=null)
                dataBase.Close();
            Application.Quit();
        }

        private void KeyPressHandler(object obj, KeyPressEventArgs args) {
            double r = Config.Zoom;

            if (args.Event.Key == Gdk.Key.KP_Add) { 
                Config.Zoom = Config.Zoom/Config.ZoomFactor;
                Zmena(r/Config.Zoom);
            }
            if (args.Event.Key == Gdk.Key.KP_Subtract) { 
                Config.Zoom = Config.Zoom*Config.ZoomFactor;
                Zmena(r/Config.Zoom);
            }
            if (args.Event.Key == Gdk.Key.KP_Multiply) { 
                Config.Zoom = 1.0;
                Zmena(r);
            }
            if (args.Event.Key == Gdk.Key.KP_Divide) { 
                Config.DrawMap = !Config.DrawMap;
                OnExposed(null, null);
            }
        }

        private void OnDrawMapActivate(object obj, EventArgs args) {
            Config.DrawMap = ((CheckMenuItem)obj).Active;
            OnExposed(null, null);
        }

        private void ButtonPressHandler(object obj, ButtonPressEventArgs args) {
            double x, y;
            Orienteering.GUI.Standard.
                MapPoint(args.Event.X, args.Event.Y, scrolledwindow1, out x, out y);
            int tmp=0;

            if (Config.Mode == Config.GUIMode.Start) {
                try {
                    tmp = Orienteering.GUI.Standard.Code(window1, "Zadajte číslo štartu");
                    comp.DrawStage.AddObject(new Start(x, y, tmp, comp.DrawStage), dataBase);
                    OnExposed(null, null);
                }
                catch (AlreadyExists) {
                    Standard.Error("Štart č. "+tmp.ToString()+" už existuje");
                }
                catch (None) {
                }
            }

            if (Config.Mode == Config.GUIMode.Control) {
                try {
                    tmp = Orienteering.GUI.Standard.Code(window1, "Zadajte číslo kontroly");
                    comp.DrawStage.AddObject(new Control(x, y, tmp, comp.DrawStage), dataBase);
                    OnExposed(null, null);
                }
                catch (AlreadyExists) {
                    Standard.Error("Kontrola č. "+tmp.ToString()+" už existuje");
                }
                catch (None) {
                }
            }

            if (Config.Mode == Config.GUIMode.Finish) {
                try {
                    tmp = Orienteering.GUI.Standard.Code(window1, "Zadajte číslo cieľa");
                    comp.DrawStage.AddObject(new Finish(x, y, tmp, comp.DrawStage), dataBase);
                    OnExposed(null, null);
                }
                catch (AlreadyExists) {
                    Standard.Error("Cieľ č. "+tmp.ToString()+" už existuje");
                }
                catch (None) {
                }
            }

            Config.Mode = Config.GUIMode.None;

            drawingarea1.GdkWindow.Cursor =
                new Gdk.Cursor(Gdk.CursorType.LeftPtr);
        }

        void OnExposed (object o, ExposeEventArgs args) {
            if (zmena_expose) {
                System.Drawing.Graphics gra =
                    Gtk.DotNet.Graphics.FromDrawable(drawingarea1.GdkWindow, true);

                gra.SmoothingMode = SmoothingMode.HighQuality;
                gra.ScaleTransform((float)Config.Zoom, (float)Config.Zoom);

                comp.DrawStage.Draw(gra, dataBase);
            }
        }

        private void OnButton1Pressed (object o, EventArgs args) {
            drawingarea1.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Cross);
            Config.Mode = Config.GUIMode.Start;
        }
        private void OnButton2Pressed (object o, EventArgs args) {
            drawingarea1.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Cross);
            Config.Mode = Config.GUIMode.Control;
        }
        private void OnButton3Pressed (object o, EventArgs args) {
            drawingarea1.GdkWindow.Cursor = new Gdk.Cursor(Gdk.CursorType.Cross);
            Config.Mode = Config.GUIMode.Finish;
        }

        private void OnZoomInPressed (object o, EventArgs args) {
            double r = Config.Zoom;
            Config.Zoom = Config.Zoom/Config.ZoomFactor;
            Zmena(r/Config.Zoom);
        }

        private void OnZoomOutPressed (object o, EventArgs args) {
            double r = Config.Zoom;
            Config.Zoom = Config.Zoom*Config.ZoomFactor;
            Zmena(r/Config.Zoom);
        }

        private void OnZoomEditingDone(object o, EventArgs args) {
            string s = entry1.Text;
            double r = Config.Zoom;
            try {
                if (s.TrimEnd(null).EndsWith("%") ) {
                    Config.Zoom = Convert.ToDouble(s.Remove(s.LastIndexOf('%'), 1))/100;
                    Zmena(r/Config.Zoom);
                }
                else throw new Exception();
            }
            catch {
                entry1.Text = Math.Round(100*Config.Zoom).ToString()+"%";
            }
        }

        private void OnNewActivate (object o, EventArgs args) {
            try {
                string name = Orienteering.GUI.Standard.Name(window1, "Zadajte názov nových pretekov");
                int count = Orienteering.GUI.Standard.Code(window1, "Zadajte počet etáp");

                OrComp.DataBase db = new OrComp.DataBase();
                OrComp.Competition competition = new OrComp.Competition(name, db);
                for (int i=0; i<count; i++)
                    new OrComp.Stage(i+1, "", competition, db);
                comp = new Competition(competition);
                dataBase = new DataBase(db);
                comp.DrawStage = comp.Stage(1, dataBase);
            }
            catch (None) {
            }
        }

        private void OnStageActivate(object o, EventArgs args) {
            try {
                int number = Orienteering.GUI.Standard.Code(window1, "Zadajte číslo etapy, ktorá sa má zobrazovať");
                comp.DrawStage = comp.Stage(number, dataBase);
                OnExposed(null, null);
            }
            catch (None) {
            }
            catch (DoesntExist) {
                Orienteering.GUI.Standard.Error("Taká etapa neexistuje");
            }
        }

        private void OnCourseActivate (object o, EventArgs args) {
            Dialog dialog = new Dialog("Trate", window1, DialogFlags.DestroyWithParent);
            dialog.VBox.Add(comp.DrawStage.Courses(dataBase));
            dialog.AddButton("OK", ResponseType.Ok);
            dialog.AddButton("Cancel", ResponseType.Cancel);

            dialog.Modal = true;
            dialog.Resize(400, 600);
            dialog.ShowAll();
            if (dialog.Run() == (int)ResponseType.Ok) {
                OnExposed(null, null);
                comp.DrawStage.Save(dataBase);
            }
            else {
            
            }
            dialog.Destroy();
        }

        private void OnCategoriesActivate (object o, EventArgs args) {
            Dialog dialog = new Dialog("Kategórie", window1, DialogFlags.DestroyWithParent);
            dialog.VBox.Add(comp.DrawStage.Categories(dataBase));
            dialog.AddButton("OK", ResponseType.Ok);
            dialog.AddButton("Cancel", ResponseType.Cancel);

            dialog.Modal = true;
            dialog.Resize(400, 600);
            dialog.ShowAll();
            dialog.Run();
            dialog.Destroy();
        }

        private void OnRunnersActivate(object o, EventArgs args) {
            Dialog dialog = new Dialog("Pretekari", window1, DialogFlags.DestroyWithParent);
            dialog.VBox.Add(comp.Runners(dataBase));
            dialog.AddButton("OK", ResponseType.Ok);
            dialog.AddButton("Cancel", ResponseType.Cancel);

            dialog.Modal = true;
            dialog.Resize(400, 600);
            dialog.ShowAll();
            if (dialog.Run() == (int)ResponseType.Ok) {
            }
            else {
            
            }
            dialog.Destroy();
        }

        private void OnDrawTimesActivate(object o, EventArgs args) {
            foreach (OrComp.Category category in comp.DrawStage.data.Categories(dataBase.data))
                category.DrawTimes(dataBase.data);
            Standard.Info("Štartové časy úspešne vylosované");
        }

        private List<OrComp.Start> starts;
        private List<OrComp.Finish> finishes;
        ListStore ControlListStore, ObjectListStore;
        TreeView tree1, tree2;
        private void OnAddCourseActivate (object o, EventArgs args) {
            string name = null;
            try {
                name = Orienteering.GUI.Standard.Name(window1, "Zadajte názov novej trate");
                comp.DrawStage.Course(name, dataBase);
                Orienteering.GUI.Standard.Error("Trať "+name+" už existuje");
            }
            catch (None) {
            }
            catch (DoesntExist) {
                OrComp.Course course = new OrComp.Course(name, comp.DrawStage.data);

                starts = comp.DrawStage.data.Starts(dataBase.data);
                finishes = comp.DrawStage.data.Finishes(dataBase.data);

                Dialog dialog = new Dialog("Nová trať", window1, DialogFlags.DestroyWithParent);
                HBox hbox1 = new HBox();
                hbox1.Add(new Label("Štart:"));
                ComboBox combo1 = comp.DrawStage.Starts(dataBase);
                hbox1.Add(combo1);

                hbox1.Add(new Label("Cieľ:"));
                ComboBox combo2 = comp.DrawStage.Finishes(dataBase);
                hbox1.Add(combo2);

                HBox hbox2 = new HBox();

                ControlListStore = new ListStore(typeof(string));
                foreach (OrComp.Control control in comp.DrawStage.data.Controls(dataBase.data))
                    ControlListStore.AppendValues(control.Number.ToString());
                tree1 = new TreeView(ControlListStore);
                tree1.EnableGridLines = TreeViewGridLines.Both;
                tree1.RulesHint = true;
                tree1.AppendColumn ("Kontroly", new Gtk.CellRendererText (), "text", 0);
                Viewport viePo1 = new Viewport();
                viePo1.Add(tree1);
                ScrolledWindow viewer1 = new ScrolledWindow();
                viewer1.Add(viePo1);
                hbox2.PackStart(viewer1, true, true, 1);

                VBox vbox2 = new VBox();
                Button button3 = new Button("+"), button4 = new Button("-");
                button3.Clicked += OnAddControl2Course;
                vbox2.Add(button3);
                button4.Clicked += OnDelControl;
                vbox2.Add(button4);
                hbox2.PackStart(vbox2, false, false, 2);

                ObjectListStore = new ListStore(typeof(string));
                tree2 = new TreeView(ObjectListStore);
                tree2.EnableGridLines = TreeViewGridLines.Both;
                tree2.RulesHint = true;
                tree2.AppendColumn ("Trať", new Gtk.CellRendererText (), "text", 0);
                Viewport viePo2 = new Viewport();
                viePo2.Add(tree2);
                ScrolledWindow viewer2 = new ScrolledWindow();
                viewer2.Add(viePo2);
                hbox2.PackStart(viewer2, true, true, 1);

                dialog.VBox.PackStart(hbox1, false, false, 2);
                dialog.VBox.PackStart(hbox2, true, true, 2);
                dialog.AddButton("OK", ResponseType.Ok);
                dialog.AddButton("Cancel", ResponseType.Cancel);

                dialog.Modal = true;
                dialog.ShowAll();
                dialog.Resize(400, 600);

                if (dialog.Run() == (int)ResponseType.Ok) {
                    course.AddObject(
                        comp.DrawStage.data.Start(Convert.ToInt32(combo1.ActiveText), dataBase.data)
                    );

                    TreeIter iter;
                    ObjectListStore.GetIterFirst(out iter);
                    do {
                        course.AddObject(
                            comp.DrawStage.data.Control(Convert.ToInt32(ObjectListStore.GetValue(iter, 0)), dataBase.data)
                        );
                    } while (ObjectListStore.IterNext(ref iter));
                    
                    course.AddObject(
                        comp.DrawStage.data.Finish(Convert.ToInt32(combo2.ActiveText), dataBase.data)
                    );
                    course.Save(dataBase.data);
                }
                dialog.Destroy();
            }
        }

        private void OnAddControl2Course(object o, EventArgs args) {
            TreePath tp; TreeViewColumn tvc;
            tree1.GetCursor(out tp, out tvc);
            if (tp != null) {
                TreeIter iter;
                ControlListStore.GetIter(out iter, tp);
                ObjectListStore.AppendValues((string)ControlListStore.GetValue(iter, 0));
            }
        }

        private void OnDelControl(object o, EventArgs args) {
            TreePath tp; TreeViewColumn tvc;
            tree2.GetCursor(out tp, out tvc);
            if (tp != null) {
                TreeIter iter;
                ObjectListStore.GetIter(out iter, tp);
                ObjectListStore.Remove(ref iter);
            }
        }

        private void OnSaveAsActivate (object o, EventArgs args) {
            FileChooserDialog fs=
                new FileChooserDialog(Catalog.GetString("Save File As"), window1, FileChooserAction.Save,
                        Stock.Cancel, ResponseType.Cancel,
                        Stock.Save, ResponseType.Accept);

            ResponseType response = (ResponseType)fs.Run();
            fs.Hide();
            if (response == ResponseType.Accept) {
                comp.Save(fs.Filename, dataBase);
                opened_file = fs.Filename;
            }

            fs.Destroy();	
        }

        private void OnSaveActivate (object o, EventArgs args) {
            comp.Save(opened_file, dataBase);
        }
        
        private void OnExportStartListActivate(object o, EventArgs args) {
            FileChooserDialog fs=
                new FileChooserDialog(Catalog.GetString("Export Startlist"), window1, FileChooserAction.Save,
                        Stock.Cancel, ResponseType.Cancel,
                        Stock.Save, ResponseType.Accept);

            ResponseType response = (ResponseType)fs.Run();
            fs.Hide();
            if (response == ResponseType.Accept)
                comp.DrawStage.data.StartList(fs.Filename, dataBase.data);
            fs.Destroy();	
        }

        private void OnExportResultsActivate(object o, EventArgs args) {
            FileChooserDialog fs=
                new FileChooserDialog(Catalog.GetString("Export Startlist"), window1, FileChooserAction.Save,
                        Stock.Cancel, ResponseType.Cancel,
                        Stock.Save, ResponseType.Accept);

            ResponseType response = (ResponseType)fs.Run();
            fs.Hide();
            if (response == ResponseType.Accept)
                comp.DrawStage.data.Results(fs.Filename, dataBase.data);
            fs.Destroy();
        }

        private void OnMapChangeActivate(object o, EventArgs args) {
            FileChooserDialog fs=
                new FileChooserDialog(Catalog.GetString("Open map"), window1, FileChooserAction.Save,
                        Stock.Cancel, ResponseType.Cancel,
                        Stock.Ok, ResponseType.Accept);

            ResponseType response = (ResponseType)fs.Run();
            fs.Hide();
            if (response == ResponseType.Accept) {
                comp.DrawStage.MapPath = fs.Filename;
                Zmena(1);
            }
            fs.Destroy();	
        }

        private void OnAddResultActivate(object o, EventArgs args) {
            Dialog dialog = new Dialog("Pridať výsledok", window1, DialogFlags.DestroyWithParent);
            dialog.AddButton("OK", ResponseType.Ok);
            dialog.AddButton("Cancel", ResponseType.Cancel);

            Table table = new Table(2, 3, false);
            table.Attach(new Label("Meno"), 0, 1, 0, 1);
            table.Attach(new Label("Dobehový čas"), 1, 3, 0, 1);
            table.Attach(new Label("Typ výsledku"), 3, 4, 0, 1);

            List<OrComp.Result> results = new List<OrComp.Result>();
               
            foreach (OrComp.Category category in comp.DrawStage.data.Categories(dataBase.data))
                foreach (OrComp.Result result in category.Results(dataBase.data))
                    results.Add(result);

            List<Widget> zoz = new List<Widget>();
            ComboBox combo1 = ComboBox.NewText();
            ComboBox combo2 = ComboBox.NewText();
            combo2.AppendText("Disq");
            combo2.AppendText("OK");
            foreach (OrComp.Result result in results)
                combo1.AppendText(result.Runner.Name);

            zoz.Add(combo1);
            zoz.Add(new Entry());
            zoz.Add(new Entry());
            zoz.Add(combo2);

            for (int i = 0; i < zoz.Count; i++) {
                if (i > 0 && i < 3)
                    (zoz[i] as Entry).IsEditable = true;
                table.Attach(zoz[i], (uint)i, (uint)i+1, 1, 2);
            }

            dialog.VBox.Add(table);
            dialog.Modal = true;
            dialog.ShowAll();
            if ((ResponseType)dialog.Run() == ResponseType.Ok) {
                OrComp.Result result = comp.DrawStage.data.Result(combo1.ActiveText, dataBase.data);
                if (combo2.Active == 0)
                    result.ResultType = ResultType.Disq;
                if (combo2.Active == 1) {
                    result.ResultType = ResultType.Ok;
                    result.Finish = comp.DrawStage.data.StartTime
                        .AddMinutes( Convert.ToInt32((zoz[1] as Entry).Text) )
                        .AddSeconds( Convert.ToInt32((zoz[2] as Entry).Text) );
                }

                result.Save(dataBase.data);
            }
            dialog.Destroy();
        }

        private void OnOpenActivate (object o, EventArgs args) {
            FileChooserDialog OpenFile =
                new FileChooserDialog(Catalog.GetString("Open File"), window1, FileChooserAction.Open,
                        Stock.Cancel, ResponseType.Cancel,
                        Stock.Open, ResponseType.Accept);
            OpenFile.Modal = true;

            bool finished = false;
            bool open = false;
            string fileName = "";

            while (!finished) {
                int result = OpenFile.Run();
                switch (result) {
                    case (int)ResponseType.Accept:
                        fileName = OpenFile.Filename;

                        if (System.IO.File.Exists (fileName)) {
                            open = true;
                            finished = true;
                        }
                        break;
                    case (int)ResponseType.Cancel:
                        finished = true;
                        break;
                }
            }
            OpenFile.Hide();

            if (open) {
                try {
                    dataBase = new DataBase();
                    comp = Orienteering.GUI.Competition.Open(fileName, dataBase);
                    comp.DrawStage = comp.Stage(1, dataBase);
                    opened_file = fileName;
                    Zmena(1);
                    OnExposed(null, null);
                }
                catch {
                    Orienteering.GUI.Standard.Error("Zlyhalo otvorenie suboru");
                }
            }
        }
    }
}
