using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

using System.IO;
using System.Collections;

namespace MapClient
{
    // This is main class of application
    public class SimpleMapClient
    {
        public const string DEFAULT_SERVER_NAME = "tornado.emapa.pl";
        public const int DEFAULT_SERVER_PORT = 6090;
        public const string DEFAULT_SERVER_PATH = "/soap/IMapCenterService";       
        public const string DEFAULT_USER_NAME = "user";

        // windows
        private HelloWindow helloWindow;
        private MainWindow mainWindow;

        // variables connected with SOAP service
        // parameters of rendering map
        private TSoapTLongLatPoint middlePoint;
        private MapParameters mapParameters;                        // contains: altitude, rotation, tilt, type of projection, visible layers
        private IMapService mapService;

        // Default constructor
        public SimpleMapClient()
        {
            // preparing hello window to have a place for messages
            helloWindow = new HelloWindow(this);
            helloWindow.setServerName(DEFAULT_SERVER_NAME);
            helloWindow.setServerPort(DEFAULT_SERVER_PORT);
            helloWindow.setUserName(DEFAULT_USER_NAME);            
            helloWindow.Show();
            helloWindow.addMessageLine("Welcome in simple map client");

            mapParameters = new MapParameters();
        }

        // This function connects to server; returns true if succeeded
        public bool connectToServer()
        {
            helloWindow.addMessage("Connecting to map serwer...");
            string serverName = "http://" + helloWindow.getServerName() + ":" + helloWindow.getServerPort() + DEFAULT_SERVER_PATH;
            mapService = new IMapService(serverName, helloWindow.getUserName(), helloWindow.getUserPassword());
            
            // creating connection to map server and opening session
            if (!mapService.beginSession())
            {
                helloWindow.addMessageLine("Connection broken");
                return false;
            }
            
            helloWindow.addMessageLine("OK");
            
            // preparing main window
            mainWindow = new MainWindow(this);
            
            // you can set here values of latitude and longtitude 
            middlePoint = new TSoapTLongLatPoint();
            middlePoint.Latitude = 51.783333;                   // default: latitude of Lodz
            middlePoint.Longitude = 19.466667;                  // default: longtitude of Lodz    
            mainWindow.setLatitude(middlePoint.Latitude);
            mainWindow.setLongitude(middlePoint.Longitude);
            
            // default values shown in main window at the beginning
            mainWindow.setRotation(mapParameters.Rotation);
            mainWindow.setRotationMin(0.0);
            mainWindow.setRotationMax(360.0);
            mainWindow.setAltitudeMin(0.0);
            mainWindow.setAltitudeMax(1300000.0);
            mainWindow.setAltitude(mapParameters.Altitude);
            mainWindow.setTilt(mapParameters.Tilt);
            mainWindow.setTiltMin(-30.0);
            mainWindow.setTiltMax(30.0);
            mainWindow.setAvailableLayers(mapService.getAvailableLayers());
            mainWindow.setAllLayers();
            mainWindow.setAvailableProjections(mapService.getAvailableProjections());
            mainWindow.setSelectedProjection(6);
            mainWindow.disableCityList();
            mainWindow.disableStreetList();

            // downloads current map from map server
            refreshMap();

            return true;
        }
        
        // This function downloads map using current parameters set
        public void refreshMap()
        {
            helloWindow.addMessage("Downloading map...");
            // We set cursor for wait that even we don't see hello window, we know that aplication didn't hang up
            mainWindow.Cursor = Cursors.WaitCursor;

            middlePoint.Latitude = mainWindow.getLatitude();
            middlePoint.Longitude = mainWindow.getLongitude();
   
            mapParameters.Altitude = mainWindow.getAltitude();
            mapParameters.Rotation = mainWindow.getRotation();
            mapParameters.Tilt = mainWindow.getTilt();
            mapParameters.TypeOfProjection = mainWindow.getSelectedProjection();
            mapParameters.VisibleLayers = mainWindow.getSelectedLayers();

            // download map from server
            TSoapRenderMapOnImageByPoint__mcsResult image = mapService.renderByPoint(
                mapParameters, 
                middlePoint, 
                mainWindow.getDisplayPanelWidth(), 
                mainWindow.getDisplayPanelHeight());
            
            if (image.SoapResult == 1)
            {
                System.Drawing.Image _image = System.Drawing.Image.FromStream(new System.IO.MemoryStream(image.BitmapImage));
                mainWindow.setImage(_image);
                helloWindow.addMessageLine("OK");
            }
            else
            {
                helloWindow.addMessageLine("Error" + image.SoapResult + "");
            }

            mainWindow.Cursor = Cursors.Default;
        }
        
        
        public void searchCity()
        {
            // Before searching city, you must clear cities list
            mainWindow.clearCities();
            mainWindow.clearSelectedStreet(); 

            // Initializing search module
            helloWindow.addMessage("Initializing search module...");
            int searchInitialize = mapService.searchInitialize();
            
            // checks whether we're succeeded
            if(searchInitialize == 1)           
            {                
                helloWindow.addMessageLine("OK");
                
                string nameOfCity = mainWindow.getSearch();

                if(nameOfCity.Equals(""))       //Empty name of searching city
                {
                    helloWindow.addMessageLine("City was not found");
                    mainWindow.addAvailableCity("City was not found");
                    mainWindow.disableCityList();
                }
                else
                {
                    // Number of found cities
                    int count = mapService.searchForCity(nameOfCity);

                    TSoapSearchGetCityList__mcsResult cities = mapService.getFoundCityList(count);  
                    mainWindow.enableCityList();

                    for (int i = 0; i < cities.CityNames.Length; i++)
                    {
                        mainWindow.addAvailableCity(cities.CityNames[i] + ", Country: " + cities.CityAdmNames[i][0] + ", County: " + cities.CityAdmNames[i][1] + ", District: " + cities.CityAdmNames[i][2] + ", Commune: " + cities.CityAdmNames[i][3]);
                    }
                }
            }
            else
            {
                helloWindow.addMessageLine("Error: " + searchInitialize + "");
            }
        }


        // This funcion gets which city is selected and renders a map using ractangle which is bounding area of selected city
        public void goToSelectedCity()
        {
            int whichCity = mainWindow.getSelectedCity();               // An index of selected city
            
            mainWindow.clearSelectedStreet();

            helloWindow.addMessage("Adding city to session...");
            TSoapSearchAddCityToSelection__mcsResult addingCity = mapService.addingCity(whichCity);

            if (addingCity.SoapResult == 1)
            {
                helloWindow.addMessageLine("OK");

                // checks if we need to find a street in city
                if (mainWindow.getSearchStreet().Equals(""))            // Empty name of searching street 
                {
                    mainWindow.disableStreetList();
                }
                else
                {
                    searchStreet(whichCity);  
                }
                // downloads map
                refreshMapUsingRect(addingCity.BoundingRect);
            }
            else
            {
                helloWindow.addMessageLine("Error: " + addingCity.SoapResult + "");
            }
        }

        // This funcion renders map by given geographical rectangle, it uses top-left and bottom-right coordinates to render a map
        public void refreshMapUsingRect(TSoapTLongLatRect longLatRect)
        {
            // updating map parameters with values from window
            mapParameters.Altitude = mainWindow.getAltitude();
            mapParameters.Rotation = mainWindow.getRotation();
            mapParameters.Tilt = mainWindow.getTilt();
            mapParameters.TypeOfProjection = mainWindow.getSelectedProjection();
            mapParameters.VisibleLayers = mainWindow.getSelectedLayers();

            helloWindow.addMessage("Downloading map...");
            mainWindow.Cursor = Cursors.WaitCursor;

            TSoapRenderMapOnImageByRect__mcsResult image = mapService.renderByRect(
                mapParameters,
                longLatRect,
                mainWindow.getDisplayPanelWidth(),
                mainWindow.getDisplayPanelHeight()
                );

            if (image.SoapResult == 1)
            {
                if (image.MapAltitude < 1300000.0)
                {
                    mainWindow.setAltitude(image.MapAltitude);
                    mainWindow.setLatitude(image.MiddlePoint.Latitude);
                    mainWindow.setLongitude(image.MiddlePoint.Longitude);

                    System.Drawing.Image _image = System.Drawing.Image.FromStream(new System.IO.MemoryStream(image.BitmapImage));
                    mainWindow.setImage(_image);
                    helloWindow.addMessageLine("OK");
                    mainWindow.Cursor = Cursors.Default;
                }
                else
                {
                    helloWindow.addMessageLine("Error, picture cannot be smaller"); 
                    mainWindow.Cursor = Cursors.Default;
                    refreshMap();
                }
            }
            else
            {
                helloWindow.addMessageLine("Error: " + image.SoapResult + "");
            }
        }

        // This function searches street in city selected by user from list of found cities
        public void searchStreet(int inWhichCity)
        {
            string streetName = mainWindow.getSearchStreet();

            helloWindow.addMessage("Street searching...");
            
            int count = mapService.searchForStreet(inWhichCity, streetName);
            
            if (count != 0)
            {
                helloWindow.addMessageLine("OK");

                TSoapSearchGetItemsList__mcsResult itemList = mapService.foundStreet(count);

                mainWindow.enableStreetList();
                for (int i = 0; i < itemList.ItemNames.Length; i++)
                    mainWindow.addAvailableStreet(itemList.ItemNames[i]);
            }
            else
            {
                helloWindow.addMessageLine("Street was not found");
                mainWindow.addAvailableStreet("Street was not found");
                mainWindow.disableStreetList();
                
            }
        }

        // this method moves map at point of requested street number (if possible) or at selected street
        public void goToSelectedStreet()
        {
            int whichStreet = mainWindow.getSelectedStreet();

            helloWindow.addMessage("Adding street to session...");

            if (mainWindow.getStreetNumber().Equals("")) // Empty street number
            {
                TSoapSearchAddObjectToSelection__mcsResult selectedStreet = mapService.addStreet(whichStreet);
                if (selectedStreet.SoapResult == 1)
                {
                    helloWindow.addMessageLine("OK");
                    // zoom at rectangle containg selected street
                    refreshMapUsingRect(selectedStreet.BoundingRect); 
                }
                else
                {
                    helloWindow.addMessageLine("Error: " + selectedStreet.SoapResult + "");
                }
            }
            else
            {
                // tries to center at selected  street number
                string streetNum = mainWindow.getStreetNumber(); 
                TSoapSearchAddStreetWithNumToSelection__mcsResult selectedStreetWithNum = mapService.addStreetWithNum(whichStreet, streetNum);

                if (selectedStreetWithNum.SoapResult == 1)
                {
                    helloWindow.addMessageLine("OK");
                    mainWindow.setLatitude(selectedStreetWithNum.MidPoint.Latitude); 
                    mainWindow.setLongitude(selectedStreetWithNum.MidPoint.Longitude);
                    mainWindow.setAltitude(60.0);
                    refreshMap();
                }
                else
                {
                    TSoapSearchAddObjectToSelection__mcsResult selectedStreet = mapService.addStreet(whichStreet);
                    
                    if (selectedStreet.SoapResult == 1)
                    {
                        mainWindow.clearNumberTextBox();
                        helloWindow.addMessageLine("OK"); 
                        helloWindow.addMessageLine("Found street have not that number");
                        refreshMapUsingRect(selectedStreet.BoundingRect);
                    }
                    else
                    {
                        helloWindow.addMessageLine("Error: " + selectedStreet.SoapResult + "");
                    }
                }
            }
        }


        public Form getMainWindow()
        {
            return mainWindow;
        }
        

        public Form getHelloWindow()
        {
            return helloWindow;
        }


        public void showHelloWindow()
        {
            helloWindow.Show();
        }

        // This metod is used to zoom on map; arguments define a rectagle to be zoom into.
        public void zoomTo(int x, int y, int width, int height)
        {
            // updates map parameters
            middlePoint.Latitude = mainWindow.getLatitude();
            middlePoint.Longitude = mainWindow.getLongitude();
            
            mapParameters.Altitude = mainWindow.getAltitude();
            mapParameters.Rotation = mainWindow.getRotation();
            mapParameters.Tilt = mainWindow.getTilt();
            mapParameters.TypeOfProjection = mainWindow.getSelectedProjection();
            mapParameters.VisibleLayers = mainWindow.getSelectedLayers();

            // convert screen point into geographics point
            TSoapTPoint[] pointsForConverting = { new TSoapTPoint(), new TSoapTPoint() };

            pointsForConverting[0].X = x;
            pointsForConverting[0].Y = y;
            pointsForConverting[1].X = x + width;
            pointsForConverting[1].Y = y + height;

            helloWindow.addMessage("Creating new point...");

            TSoapConvertScreenToMap__mcsResult convertedPoints = mapService.pointsConverting( 
                                                mapParameters,
                                                mainWindow.getDisplayPanelHeight(),
                                                mainWindow.getDisplayPanelWidth(),
                                                middlePoint,
                                                pointsForConverting);

            if (convertedPoints.SoapResult == 1)
            {
                // if conversion is succedded ....
                helloWindow.addMessageLine("OK");
                TSoapTLongLatRect rect = new TSoapTLongLatRect();
                rect.TopLeft = convertedPoints.MapPoints[0]; 
                rect.BottomRight = convertedPoints.MapPoints[1];
                // ... we can download map
                refreshMapUsingRect(rect);
            }
            else
            {
                helloWindow.addMessageLine("Error: " + convertedPoints.SoapResult + "");
            }
        }

        // centres map at given screen point
        public void centerAt(int x, int y)
        {
            // updates map parameters
            middlePoint.Latitude = mainWindow.getLatitude();
            middlePoint.Longitude = mainWindow.getLongitude();
            
            mapParameters.Altitude = mainWindow.getAltitude();
            mapParameters.Rotation = mainWindow.getRotation();
            mapParameters.Tilt = mainWindow.getTilt();
            mapParameters.TypeOfProjection = mainWindow.getSelectedProjection();
            mapParameters.VisibleLayers = mainWindow.getSelectedLayers();


            // converting screen point into geographics coordinations
            TSoapTPoint[] pointsForConverting = { new TSoapTPoint() };

            pointsForConverting[0].X = x;
            pointsForConverting[0].Y = y;

            helloWindow.addMessage("Creating new point...");
            
            TSoapConvertScreenToMap__mcsResult convertedPoints = mapService.pointsConverting(
                                                                mapParameters,
                                                                mainWindow.getDisplayPanelHeight(),
                                                                mainWindow.getDisplayPanelWidth(),
                                                                middlePoint,
                                                                pointsForConverting);

            if (convertedPoints.SoapResult == 1)
            {
                // if conversion was succedded, update window values and download map
                helloWindow.addMessageLine("OK");
                mainWindow.setLatitude(convertedPoints.MapPoints[0].Latitude);
                mainWindow.setLongitude(convertedPoints.MapPoints[0].Longitude);
                refreshMap();
            }
            else
            {
                helloWindow.addMessageLine("Error: " + convertedPoints.SoapResult + "");
            }
        }

        //Funcion to clear map from searched objects added to server session
        public void clearMapFromSearchedObject()
        {
            mapService.clearMapFromAddItem();
            refreshMap();
        }

        // functions opens csv file with data to be geocode
        public void openFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Comma Separated Values (*.csv)|*.csv"; 
            dialog.Title = "Open file";

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                mainWindow.clearOpenFileDataGrid();

                StreamReader objReader = new StreamReader(dialog.FileName, Encoding.GetEncoding("Windows-1250"));

                string sLine = "";

                List<string> textArray = new List<string>();            //to read file line by line

                while (sLine != null)
                {
                    sLine = objReader.ReadLine();
                    if (sLine != null)
                    {
                        textArray.Add(sLine);
                    }
                }
                objReader.Close();
                
                string[] wordsInLine = new string[7];               //7 words at one line
                string[] words = new string[textArray.Count * 7];
                
                int i = 0;                  
                
                for (int j = 0; j < textArray.Count; j++)
                {
                    wordsInLine = textArray[j].Split(new char[] { ';' });
                    
                    //putting words from the line to words array
                    words[i] = wordsInLine[0];
                    words[i + 1] = wordsInLine[1];
                    words[i + 2] = wordsInLine[2];
                    words[i + 3] = wordsInLine[3];
                    words[i + 4] = wordsInLine[4];
                    words[i + 5] = wordsInLine[5];
                    words[i + 6] = wordsInLine[6];
                    i = i + 7;
                    mainWindow.addToGrid(wordsInLine);
                }
                
                geokodingReadFile(words);
            }
            else
            {
                helloWindow.addMessageLine("There was no file opened");            
            }
        }

        // This function geocodes given data
        public void geokodingReadFile(string[] words)
        {

            helloWindow.addMessage("Searching for geographic coordinates....");
            
            TSoapGeocode__mcsResult geocodResult = mapService.geocoding(words);

            if (geocodResult.SoapResult == 1)
            {
                helloWindow.addMessageLine("OK");
                // geocoded object can be localized and marked at map 
                localizeObject(geocodResult);               
            }
            else
            {
                helloWindow.addMessageLine("Error: " + geocodResult.SoapResult + "");
            }
        }

        //This function creates an object on server session which contains earlier geocoded location 
        public void localizeObject(TSoapGeocode__mcsResult geocodResult)
        {
            
            helloWindow.addMessage("Localize...");                                                 
            int localizeResult = mapService.localize();                                                     

            if (localizeResult == 1)
            {
                // if succedded, adding a mark on map
                helloWindow.addMessageLine("OK");
                helloWindow.addMessage("Adding positions to object...");

                int positionAddResult = mapService.localizeAddPosition(geocodResult.Positions);

                if (positionAddResult == 1)
                {
                    helloWindow.addMessageLine("OK");
                    refreshMap();
                    
                }
                else
                {
                    helloWindow.addMessageLine("Error: " + positionAddResult + "");
                }
            }
            else
            {
                helloWindow.addMessageLine("Error: " + localizeResult + "");
            }
        }

        // moves map to place selected by item in openFileGridView
        public void goToLocationSelectedInGrid(int rowIndex)
        {
            if (rowIndex != -1)         // -1 means empty selection
            {

                string[] text = mainWindow.getFromOpenFileDataGrid(rowIndex);               // gets data from openFileGridView
                TSoapGeocode__mcsResult geocodeResult = mapService.geocoding(text);
                
                if (geocodeResult.SoapResult == 1)
                {   
                    // if geocode was succedded,
                    // getting latitude and longitude of an object
                    double latitude = geocodeResult.Positions[0].Latitude;
                    double longitude = geocodeResult.Positions[0].Longitude;
                    
                    // if coordinas of geoceded point are equal to (0,0), requested place was not found
                    if (latitude != 0 && longitude != 0)
                    {
                        mainWindow.setLatitude(latitude);
                        mainWindow.setLongitude(longitude);
                        mainWindow.setAltitude(100);
                        refreshMap();
                    }
                    else
                    {
                        helloWindow.addMessageLine("Object is not found");
                    }
                }
                else
                {
                    helloWindow.addMessageLine("Error" + geocodeResult.SoapResult + "");
                }
            }
            else
            {
                helloWindow.addMessageLine("Grid is empty");
            }
        }

        // This funcion closes connection before leaving application
        public void closeSOAPConnection()
        {
            mapService.closeSession();
//            helloWindow.enableConnect();
        }

        public bool isConnected()
        {
            return mapService.isSessionAlive();
        }

        // This is entry point for thread connected with main window
        static public void mainWindowThreadMethod(object smp)
        {
            ((SimpleMapClient)smp).getMainWindow().Show();
            Application.Run(((SimpleMapClient)smp).getMainWindow());
        }
    }
}