//  
//  StreetEvent.cs
// based on ZoneEvent.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;

namespace LFSLapper
{
    //Nick Axworthy (nickax@gmail.com) - added street class to support defintion of streets of some specified width, each comprised of an abritrary number of segements
    class vector2 { //defines a 2D vector (x,y coordinate pair)
        public float x;
        public float y;

        public vector2(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        public float DistanceFromLine(vector2 a, vector2 b) {
            //Returns the orthogonal distance  of THIS point, from the infinite line through a-b
            //note - negative is 'left' of the line, positive is 'right' (or that might be the other way around)

            vector3 ab = new vector3(b - a);
            vector3 ac = new vector3(this - a);
            
            if(ac.lengthSquared() > 0)
            {
                return (ab.cross(ac).z / ab.length()); //distance of THIS point from the line + or -
            }
            else
            {
                return 0;
            }
        }

        public bool isBetween(vector2 a, vector2 b)
        {//returns wheteher THIS point lies 'between' point A and point B (by testing wether the vector from this point to A, and this point to B - 'point' in opposite directions
            vector2 v1 = this - a;
            vector2 v2 = this - b;
            float dp = (this - a).normalise().dot((this - b).normalise());
            if (dp <= 0)
            { return true; }
            else
            { return false; }
        }

        public static vector2 operator - (vector2 a, vector2 b){//overload the subtract operator to allow us to use '-' on vectors
            return new vector2(a.x-b.x, a.y-b.y);

            }
        
        public float dot ( vector2 v)
            //The vector dot product is another very useful concept - it tells us how 'similar' two (normalised) vectors are (direction wise)
            // 'dot = 1 = 'point' in exactly the same direction - 0 at right angles to one another -1 point in opposite directions (values are actually the cosine of the angle between the two vectors
        {
            return v.x * this.x + v.y * this.y;

        } 

        public vector2 normalise()
        {//normalisation, takes a vector (of any length) and divides it by its own length to create a vector in the same direction as the original, but 1 'unit' long
         // we often need to normalise vectors to get meaningful results from other vector operations Like dot product)

            float l = this.length();
            return new vector2(this.x / l,  this.y / l);
        }

        public float lengthSquared()
        { //We have a seperate function for lengthSquared to avoid the expensive square root when we can

            return (float)(Math.Pow(x, 2) + Math.Pow(y, 2));
        }

        public float length()
        {
            return (float)(Math.Sqrt(this.lengthSquared()));
        }
        
    }

    class vector3 { // a 3d vector x,y,z (the cross product operation requires 3D vectors)

        public float x;
        public float y;
        public float z;


        public vector3(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public vector3(vector2 v)
        {  //alternate constructor, makes a 'flat' 3d vector from a 2d vector 
            this.x = v.x;
            this.y = v.y;
            this.z = 0;
        }

        public vector3 cross(vector3 v) 

        {
            //The cross product of two 3D vectors is a third, orthogonal vector - whos magnitude (and direction) tells us usefull things
            //for (input) vectors lying in the XY plane, the cross product (output vector) will have no x or y

            return new vector3(this.y * v.z-this.z * v.y,this.z*v.x-this.x*v.y,this.x*v.y-this.y*v.x);
        }

        public float lengthSquared()
        { //We have a seperate function for lengthsquared to avoid the expensive square root when we can
           
            return (float)(Math.Pow(x, 2) + Math.Pow(y, 2) + Math.Pow(z, 2));
        }

        public float length()
        {
            return (float)(Math.Sqrt(this.lengthSquared()));
        }

        public vector3 normalise()
        { //normalisation, takes a vector (of any length)  and divides it by its own length to create a vector in the same direction as the original, but 1 'unit' long
            // we often need to normalise vectors to get meaningful results from other vector operations like dot product)
            float l = this.length();
            return new vector3(this.x / l, this.y / l, this.z/l);
        }
    }
    
    

    class streetEvent
    {
        public List<vector2> waypoints; // this is initialised in the constructor from the text based (delimited) description of the street which goes w,x,y,x,y,x,y....
        public float width;
        public string commandEnter = "";
        public string commandLeave = "";
        public string idTask;
       
        public streetEvent(string pidTask, string desc, string pcommandEnter,string pcommandleave)
        {
            this.idTask = pidTask;
            this.waypoints = new List<vector2>();
            
            string[] b = desc.Split(",".ToCharArray(),StringSplitOptions.None);
            this.width = 10 * float.Parse(b[0], CultureInfo.InvariantCulture.NumberFormat);


            for (int i = 1; i < b.Length-1; i+=2)
            {
                this.waypoints.Add(new vector2(
                    float.Parse(b[i], CultureInfo.InvariantCulture.NumberFormat),
                    float.Parse(b[i+1], CultureInfo.InvariantCulture.NumberFormat)
                    ));
           
            }

            this.commandEnter = pcommandEnter;
            this.commandLeave = pcommandleave;
        }

    }
    class ListStreetEvent
    {
        System.Collections.Hashtable listOfstreets = new System.Collections.Hashtable();


        public void Add(string idTask, string trackName, string desc, string commandEnter, string commandLeave)
        {
            if (!listOfstreets.ContainsKey(trackName))
            {
                listOfstreets[trackName] = new ArrayList();
            }
            (listOfstreets[trackName] as ArrayList).Add(new streetEvent(idTask, desc, commandEnter, commandLeave));
        }

        public int getMyStreet(string trackName, int x, int y)
        {
            if (listOfstreets.ContainsKey(trackName))
            {
                ArrayList streets = (ArrayList)listOfstreets[trackName];
                for (int i = 0; i < streets.Count; i++)
                {
                    streetEvent s = (streetEvent)streets[i];

                    vector2 p = new vector2(x, y);
                    vector2 pwp = null;
                    foreach (vector2 wp in s.waypoints)
                    {
                        if (pwp != null) {
                            if (p.isBetween(pwp, wp))
                            {
                                if (Math.Abs((p.DistanceFromLine(wp, pwp))) <= s.width / 2)
                                {
                                    return i;
                                }
                            }
                        }
                        pwp = wp;
                    }
                }
            }
            return -1;
        }

        public string idStreetByIdxStreet(string trackName, int idx)
        {
            if (idx == -1)
                return "NONE";
            ArrayList streets = (ArrayList)listOfstreets[trackName];
            return (streets[idx] as streetEvent).idTask;
        }
        public void Remove(string idTask, string trackName)
        {
            if ((idTask != "") && (trackName != ""))
            {
                if (listOfstreets.ContainsKey(trackName))
                {
                    //Get Arraylist with Streets
                    ArrayList streets = (ArrayList)listOfstreets[trackName];
                    for (int i = streets.Count - 1; i >= 0; i--)
                    {
                        if ((streets[i] as streetEvent).idTask == idTask)
                        {
                            //remove zone(ID) from Arraylist
                            streets.RemoveAt(i);
                        }
                    }
                }
            }
        }
        //New 25-11-2018
        public string getCommandStreetIDTask(string trackName, int idx)
        {
            ArrayList streets = (ArrayList)listOfstreets[trackName];
            return (streets[idx] as streetEvent).idTask;
        }

        public string getCommandStreetEnter(string trackName, int idx)
        {
            ArrayList streets = (ArrayList)listOfstreets[trackName];
            return (streets[idx] as streetEvent).commandEnter;
        }
        public string getCommandStreetLeave(string trackName, int idx, string idTask)
        {
            string ReturnValue = "";
            ArrayList streets = (ArrayList)listOfstreets[trackName];
            // Check if id value is higher than zonecount. (without this check, Lapper will crash)
            if (idx >= streets.Count)
            {
                ReturnValue = "";
            }
            else
            {
                //Get throught streetslist
                for (int i = streets.Count - 1; i >= 0; i--)
                {
                    //Compare IDtask with CurrPlayer.CurrIDTask
                    if ((streets[i] as streetEvent).idTask == idTask)
                    {
                        return (streets[idx] as streetEvent).commandLeave;
                    }
                }
            }
            return ReturnValue;
        }
    }
}
