using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Media;
using System.Threading;

namespace Skyffer
{
    public class Location : INotifyPropertyChanged
    {
        private static List<WeakReference> _allLocations = new List<WeakReference>();

        public static void Init()
        {
            Thread t = new Thread(RecomputeBackColor);
            t.IsBackground = true;
            t.Start();
        }

        private static void RecomputeBackColor()
        {
            while (true)
            {
                Thread.Sleep(1000);
                for (int i = _allLocations.Count - 1; i >= 0; i--)
                {
                    if (!_allLocations[i].IsAlive)
                    {
                        _allLocations.RemoveAt(i);
                        continue;
                    }
                    var temp = (_allLocations[i].Target as Location).BackColor;
                }
            }
        }

        public double Lat { get; set; }
        public double Lon { get; set; }
        public string IP { get; set; }
        public string LocationDesc { get; set; }

        private Brush _lastBackColor = Brushes.White;
        public Brush BackColor
        {
            get
            {
                Brush last = _lastBackColor;
                if ((DateTime.Now - LastSeen).TotalSeconds < 5)
                    _lastBackColor = Brushes.Gold;
                else
                    _lastBackColor = Brushes.White;
                if (last != _lastBackColor)
                    OnPropertyChanged("BackColor");
                return _lastBackColor;
            }
        }

        public DateTime FirstSeen { get; private set; }
        private DateTime _lastSeen = DateTime.MinValue;
        public DateTime LastSeen
        {
            get
            {
                return _lastSeen;
            }
            set
            {
                _lastSeen = value;
                OnPropertyChanged("LastSeen");
            }
        }
        private bool _isSelected = false;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected == value)
                    return;
                _isSelected = value;
                if (value)
                    PacketIPHelper.NotifyAdd(this);
                else
                    PacketIPHelper.NotifyRemove(this);
                OnPropertyChanged("IsSelected");
            }
        }

        public bool GeoLocated = false;
        public bool AlreadyAdded = false;

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public Location(string ip)
        {
            IP = ip;
            LastSeen = DateTime.Now;
            FirstSeen = DateTime.Now;
            GeoLocate();
            IsSelected = GeoLocated;
            _allLocations.Add(new WeakReference(this));
        }

        private Location(string[] data)
        {
            Lat = double.Parse(data[0], CultureInfo.InvariantCulture);
            Lon = double.Parse(data[1], CultureInfo.InvariantCulture);
            IP = data[2];
            LocationDesc = data[3];
            FirstSeen = DateTime.Parse(data[4], CultureInfo.InvariantCulture);
            LastSeen = DateTime.Parse(data[5], CultureInfo.InvariantCulture);
            IsSelected = true;
            IsSelected = bool.Parse(data[6]);
            GeoLocated = true;
            _allLocations.Add(new WeakReference(this));
        }

        private void GeoLocate()
        {
            long DecIP = IP2Dec(IP);
            StreamReader rdr = new StreamReader(File.OpenRead("GeoLiteCity-Blocks.csv"));
            rdr.ReadLine(); //file header
            rdr.ReadLine(); //file header
            int block = -1;
            while (!rdr.EndOfStream)
            {
                string[] line = rdr.ReadLine().Split(',');
                long start = long.Parse(line[0].Replace("\"", ""));
                long end = long.Parse(line[1].Replace("\"", ""));
                if (start <= DecIP && DecIP <= end)
                {
                    block = int.Parse(line[2].Replace("\"", ""));
                    break;
                }
            }
            if (block == -1)
            {
                Lat = double.NaN;
                Lon = double.NaN;
                GeoLocated = true;
                return;
            }
            rdr = new StreamReader(File.OpenRead("GeoLiteCity-Location.csv"));
            for (int i = 0; i < block + 1; ++i)
                rdr.ReadLine();
            string[] blockInfo = rdr.ReadLine().Split(',');
            Lat = double.Parse(blockInfo[5], CultureInfo.InvariantCulture);
            Lon = double.Parse(blockInfo[6], CultureInfo.InvariantCulture);
            LocationDesc = string.Format("{0},{1},{2},{3}", blockInfo[1].Replace("\"", ""), blockInfo[2].Replace("\"", ""), blockInfo[3].Replace("\"", ""), blockInfo[4].Replace("\"", ""));
            GeoLocated = true;
        }

        private long IP2Dec(string IP)
        {
            // split up the IP into octets and prep our string builder 
            List<string> octets = new List<string>(IP.Split('.'));
            StringBuilder ipAsHex = new StringBuilder();

            // loop through the octets and build up a hexidecimal string 
            octets.ForEach(delegate(string value)
            { ipAsHex.AppendFormat("{0:X2}", int.Parse(value)); });

            // convert the hex to decimal and return 
            long decimalIP = long.Parse(ipAsHex.ToString(), NumberStyles.HexNumber);
            return decimalIP;
        }

        internal string Serialize()
        {
            return string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}|{4}|{5}|{6}", Lat, Lon, IP, LocationDesc, FirstSeen, LastSeen, IsSelected);
        }
        internal static Location Deserialize(string data)
        {
            try
            {
                Location loc = new Location(data.Split('|'));
                return loc;
            }
            catch
            {
                return null;
            }

        }

    }
}
