// Motion Detector
//
// Copyright © Andrew Kirillov, 2005
// andrew.kirillov@gmail.com
//
namespace motion {
    using System;
    using System.Drawing;
    using System.Threading;
    using videosource;
    using System.Diagnostics;

    /// <summary>
    /// Camera class
    /// </summary>
    public class Camera {
        private IVideoSource videoSource = null;
        private IMotionDetector motionDetector = null;
        private Bitmap lastFrame = null;

        private int width = -1, height = -1;

        //
        public event EventHandler NewFrame;

        // LastFrame property
        public Bitmap LastFrame {
            get { return lastFrame; }
        }
        // Width property
        public int Width {
            get { return width; }
        }
        // Height property
        public int Height {
            get { return height; }
        }
        // FramesReceived property
        public int FramesReceived {
            get { return (videoSource == null) ? 0 : videoSource.FramesReceived; }
        }
        // BytesReceived property
        public int BytesReceived {
            get { return (videoSource == null) ? 0 : videoSource.BytesReceived; }
        }
        // Running property
        public bool Running {
            get { return (videoSource == null) ? false : videoSource.Running; }
        }
        // MotionDetector property
        public IMotionDetector MotionDetector {
            get { return motionDetector; }
            set { motionDetector = value; }
        }

        // Constructor
        public Camera(IVideoSource source)
            : this(source, null) { }
        public Camera(IVideoSource source, IMotionDetector detector) {
            this.videoSource = source;
            this.motionDetector = detector;
            videoSource.NewFrame += new CameraEventHandler(video_NewFrame);
        }

        // Start video source
        public void Start() {
            if (videoSource != null) {
                videoSource.Start();
            }
        }

        // Siganl video source to stop
        public void SignalToStop() {
            if (videoSource != null) {
                videoSource.SignalToStop();
            }
        }

        // Wait video source for stop
        public void WaitForStop() {
            // lock
            Lock("WaitForStop");
            try {
                if (videoSource != null)
                    videoSource.WaitForStop();
            }
            finally {
                Unlock();
            }
        }

        // Abort camera
        public void Stop() {
            // lock
            Lock("Stop");
            try {
                if (videoSource != null)
                    videoSource.Stop();
            }
            finally {
                Unlock();
            }
        }

        public string GotLock;
        // Lock it
        public void Lock(string Where) {
            Monitor.Enter(this);
            if (Thread.CurrentThread.Name == null)
                Thread.CurrentThread.Name = "Lock#" + Interlocked.Increment(ref thn);
            GotLock = Thread.CurrentThread.Name + " " + Thread.CurrentThread.GetHashCode() + " " + Where;
        }

        // Unlock it
        public void Unlock() {
            GotLock += "!"; // Means unlocke
            Monitor.Exit(this);
        }

        // On new frame

        public int thn; // For naming

        public object FrameLock = new Object();
        public string Gotframe;    // Debugging -- otherwise we'd use language lock/unlock primities
        public void LockFrame(string Where) {
            Monitor.Enter(FrameLock);
            if (Thread.CurrentThread.Name == null)
                Thread.CurrentThread.Name = "LockFrame#" + Interlocked.Increment(ref thn);
            Gotframe = Thread.CurrentThread.Name + " " + Thread.CurrentThread.GetHashCode() + " " + Where;
        }
        public void UnlockFrame() {
            Gotframe += "!";
            Monitor.Enter(FrameLock);
        }

        private void video_NewFrame(object sender, CameraEventArgs e) {
            try {
                Bitmap eFrame = null;
                lock (this) {
                    GotLock = Thread.CurrentThread.Name + " " + Thread.CurrentThread.GetHashCode() + " NewFrame";
                    eFrame = (Bitmap)e.Bitmap.Clone();

                    // image dimension
                    width = eFrame.Width;
                    height = eFrame.Height;

                    // apply motion detector
                    if (motionDetector != null)
                        motionDetector.ProcessFrame(ref eFrame);
                }

                //lock (FrameLock) {
                try {
                    LockFrame("NewFrame");
                    if (lastFrame != null)
                        lastFrame.Dispose();
                    lastFrame = eFrame;
                }
                finally {
                    UnlockFrame();
                }
            }
            catch (Exception ex) {
                Debug.WriteLine(ex.Message);
                Debugger.Break();
            }

            // notify client
            if (NewFrame != null)
                NewFrame(this, new EventArgs());
        }
    }
}
