// ALL RIGHTS RESERVED:
// ====================
// The contents of this file, and associated files in this directory, are 
// Copyright (C) SPT , all rights reserved, 2009.
// All software Source Code, Images, Database-Design and code, Graphics Design 
// and source files, and related content (collectively referred to as SOURCE) are 
// Copyright (c) SPT, 2009, All Rights Reserved.
// http://SerialPortTool.com

// PTZ Controller is a Registered Trademark of SerialPortTool.com.

using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Collections.Generic;
using SPT.PTZ.Core;

namespace PTZController.Logic
{
    /// <summary>
    /// Class for representing a tour element item
    /// </summary>
    public class TourItem : ICloneable
    {
        public TourItem()
        {
            _span = new Spt.Share.Data.StaySpan();
        }

        public TourItem(int camera, int preset, Spt.Share.Data.StaySpan span)
        {
            this.Camera = camera;
            this.Preset = preset;
            this.Span = span;
        }

        /// <summary>
        /// Camera index
        /// </summary>
        private int _camera;

        public int Camera
        {
            get { return _camera; }
            set { _camera = value; }
        }

        /// <summary>
        /// Preset index
        /// </summary>
        private int _preset;

        public int Preset
        {
            get { return _preset; }
            set { _preset = value; }
        }

        /// <summary>
        /// Stay span for current preset
        /// </summary>
        private Spt.Share.Data.StaySpan _span;

        public Spt.Share.Data.StaySpan Span
        {
            get { return _span; }
            set { _span = value; }
        }

        public static bool operator == (TourItem it1, TourItem it2)
        {
            return it1.Equals(it2);
        }

        public static bool operator !=(TourItem it1, TourItem it2)
        {
            return !it1.Equals(it2);
        }

        public override bool Equals(object obj)
        {
            TourItem it = obj as TourItem;

            return (this.Camera == it.Camera) &&
                (this.Preset == it.Preset) &&
                (this.Span == it.Span);
        }

        public override int GetHashCode()
        {
            return ((Camera << 16) + Preset);
        }

        public Object Clone()
        {
            return new TourItem(this.Camera, this.Preset, this.Span.Clone() as Spt.Share.Data.StaySpan);
        }

        /// <summary>
        /// Use this value for default time span
        /// </summary>
        public const int DefaultSpanValue = -1;
    }

    /// <summary>
    /// Class for maintaining a collection of tour items
    /// </summary>
    public class TourItemCollection
    {
        /// <summary>
        /// Tour items
        /// </summary>
        private List<TourItem> _items;

        public TourItemCollection()
        {
            Items = new List<TourItem>();
        }

        public List<TourItem> Items
        {
            get { return _items; }
            set { _items = value; }
        }

        public static bool operator == (TourItemCollection col1, TourItemCollection col2)
        {
            return col1.Equals(col2);
        }

        public static bool operator !=(TourItemCollection col1, TourItemCollection col2)
        {
            return !col1.Equals(col2);
        }

        public override bool Equals(object obj)
        {
            TourItemCollection col = obj as TourItemCollection;
            bool equal = false;

            if (Items.Count != col.Items.Count)
            {
                return equal;
            }

            for (int i = 0; i < Items.Count; i++)
            {
                if (Items[i] != col.Items[i])
                {
                    return equal;
                }
            }

            equal = true;
            return equal;
        }

        public override int GetHashCode()
        {
            return Items.Count;
        }

        public static TourItemCollection Load(string fileName)
        {
            TourItemCollection col = null;

            try
            {
                XmlSerializer serializer = new XmlSerializer(typeof(TourItemCollection));
                TextReader tr = new StreamReader(fileName);

                col = (TourItemCollection)serializer.Deserialize(tr);
                tr.Close();
            }
            catch (Exception)
            {
                col = new TourItemCollection();
            }

            return col;
        }

        public void Save(string fileName)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(TourItemCollection));
            TextWriter tw = new StreamWriter(fileName);
            serializer.Serialize(tw, this);
            tw.Close();
        }

        private int _counter = 0;

        private int _cursor = 0;

        /// <summary>
        /// Mark current item executed
        /// </summary>
        public int Cursor
        {
            get { return _cursor; }
            set { _cursor = value; }
        }
        /// <summary>
        /// Start tour
        /// </summary>
        public void Start(bool reset)
        {
            if (reset)
            {
                _counter = 0;
                _cursor = 0;
            }
        }

        /// <summary>
        /// Stop tour
        /// </summary>
        public void Stop()
        {
            _counter = 0;
            _cursor = 0;
        }

        /// <summary>
        /// Do tour operation
        /// </summary>
        /// <param name="controller">controller object</param>
        /// <param name="seconds">seconds after started</param>
        /// <remarks>
        /// This function checks how many seconds after started, and determine
        /// which preset it should go to, or just stay. 
        /// It should be called each second. 
        /// </remarks>
        public void Tour(Controller controller, Spt.Share.Data.StaySpan defaultSpan)
        {
            if (Items.Count == 0)
            {
                return;
            }

            if (_counter == 0)
            {
                Debug.WriteLine(
                    string.Format("[{0}] - Go to tour item {1}. ", 
                    DateTime.Now.ToString("HH:mm:ss"), 
                    _cursor));

                controller.Address = (byte)(Items[_cursor].Camera + 1);

                controller.SendCommand(
                    SPT.PTZ.Core.PtzFunction.SelectCamera, 
                    Items[_cursor].Camera + 1);
                controller.SendCommand(
                    SPT.PTZ.Core.PtzFunction.GotoPreset,
                    Items[_cursor].Preset + 1);
            }

            _counter++;

            // Time meet
            int seconds = Items[_cursor].Span.Seconds;

            if (seconds <= 0)
            {
                seconds = defaultSpan.Seconds;
            }

            if (_counter == seconds)
            {
                _counter = 0;
                _cursor++; // move to next item

                if (_cursor == Items.Count)
                {
                    _cursor = 0; // reset to first item
                }
            }
        }
    };
}
