using NTwain.Data;
using NTwain.Internals;
using NTwain.Triplets;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;

namespace NTwain
{
    /// <summary>
    /// Represents a TWAIN data source.
    /// </summary>
    public partial class TwainSource
    {
        ITwainSessionInternal _session;

        internal TwainSource(ITwainSessionInternal session, TWIdentity sourceId)
        {
            _session = session;
            Identity = sourceId;
        }

        /// <summary>
        /// Opens the source for capability negotiation.
        /// </summary>
        /// <returns></returns>
        public ReturnCode Open()
        {
            var rc = ReturnCode.Failure;
            _session.MessageLoopHook.Invoke(() =>
            {
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: OpenSource.", Thread.CurrentThread.ManagedThreadId));

                rc = _session.DGControl.Identity.OpenDS(this);
            });
            return rc;
        }

        /// <summary>
        /// Closes the source.
        /// </summary>
        /// <returns></returns>
        public ReturnCode Close()
        {
            var rc = ReturnCode.Failure;
            _session.MessageLoopHook.Invoke(() =>
            {
                Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Thread {0}: CloseSource.", Thread.CurrentThread.ManagedThreadId));

                rc = _session.DGControl.Identity.CloseDS();
                if (rc == ReturnCode.Success)
                {
                    SupportedCaps = null;
                }
            });
            return rc;
        }


        /// <summary>
        /// Enables the source to start transferring.
        /// </summary>
        /// <param name="mode">The mode.</param>
        /// <param name="modal">if set to <c>true</c> any driver UI will display as modal.</param>
        /// <param name="windowHandle">The window handle if modal.</param>
        /// <returns></returns>
        public ReturnCode StartTransfer(SourceEnableMode mode, bool modal, IntPtr windowHandle)
        {
            return _session.EnableSource(mode, modal, windowHandle);
        }

        /// <summary>
        /// Gets the source status. Only call this at state 4 or higher.
        /// </summary>
        /// <returns></returns>
        public TWStatus GetStatus()
        {
            TWStatus stat;
            _session.DGControl.Status.GetSource(out stat);
            return stat;
        }
        /// <summary>
        /// Gets the source status. Only call this at state 4 or higher.
        /// </summary>
        /// <returns></returns>
        public TWStatusUtf8 GetStatusUtf8()
        {
            TWStatusUtf8 stat;
            _session.DGControl.StatusUtf8.GetSource(out stat);
            return stat;
        }


        #region properties

        internal TWIdentity Identity { get; private set; }

        /// <summary>
        /// Gets the source's product name.
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        public string Name { get { return Identity.ProductName; } }

        /// <summary>
        /// Gets the source's manufacturer name.
        /// </summary>
        /// <value>
        /// The manufacturer.
        /// </value>
        public string Manufacturer { get { return Identity.Manufacturer; } }

        /// <summary>
        /// Gets the source's product family.
        /// </summary>
        /// <value>
        /// The product family.
        /// </value>
        public string ProductFamily { get { return Identity.ProductFamily; } }

        /// <summary>
        /// Gets the version information.
        /// </summary>
        /// <value>
        /// The version.
        /// </value>
        public TWVersion Version { get { return Identity.Version; } }

        /// <summary>
        /// Gets the supported data group.
        /// </summary>
        /// <value>
        /// The data group.
        /// </value>
        public DataGroups DataGroup { get { return Identity.DataGroup; } }

        /// <summary>
        /// Gets the supported TWAIN protocol major number.
        /// </summary>
        /// <value>
        /// The protocol major number.
        /// </value>
        public int ProtocolMajor { get { return Identity.ProtocolMajor; } }

        /// <summary>
        /// Gets the supported TWAIN protocol minor number.
        /// </summary>
        /// <value>
        /// The protocol minor number.
        /// </value>
        public int ProtocolMinor { get { return Identity.ProtocolMinor; } }


        static readonly CapabilityId[] _emptyCapList = new CapabilityId[0];

        private IList<CapabilityId> _supportedCaps;
        /// <summary>
        /// Gets the supported caps for this source.
        /// </summary>
        /// <value>
        /// The supported caps.
        /// </value>
        public IList<CapabilityId> SupportedCaps
        {
            get
            {
                if (_supportedCaps == null && _session.State > 3)
                {
                    _supportedCaps = CapGetValues(CapabilityId.CapSupportedCaps).CastToEnum<CapabilityId>(false);
                }
                return _supportedCaps ?? _emptyCapList;
            }
            private set
            {
                _supportedCaps = value;
                //OnPropertyChanged("SupportedCaps");
            }
        }


        /// <summary>
        /// Gets the triplet operations defined for control data group.
        /// </summary>
        public DGControl DGControl
        {
            get
            {
                return _session.DGControl;
            }
        }

        /// <summary>
        /// Gets the triplet operations defined for image data group.
        /// </summary>
        public DGImage DGImage
        {
            get
            {
                return _session.DGImage;
            }
        }

        /// <summary>
        /// Gets the direct triplet operation entry for custom values.
        /// </summary>
        public DGCustom DGCustom
        {
            get
            {
                return _session.DGCustom;
            }
        }

        #endregion

        //#region INotifyPropertyChanged Members

        ///// <summary>
        ///// Occurs when a property value changes.
        ///// </summary>
        //public event PropertyChangedEventHandler PropertyChanged;

        ///// <summary>
        ///// Raises the <see cref="PropertyChanged"/> event.
        ///// </summary>
        ///// <param name="propertyName">Name of the property.</param>
        //protected void OnPropertyChanged(string propertyName)
        //{
        //    var syncer = Session.SynchronizationContext;
        //    if (syncer == null)
        //    {
        //        try
        //        {
        //            var hand = PropertyChanged;
        //            if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
        //        }
        //        catch { }
        //    }
        //    else
        //    {
        //        syncer.Post(o =>
        //        {
        //            try
        //            {
        //                var hand = PropertyChanged;
        //                if (hand != null) { hand(this, new PropertyChangedEventArgs(propertyName)); }
        //            }
        //            catch { }
        //        }, null);
        //    }
        //}

        //#endregion

        #region cameras

        /// <summary>
        /// Gets the cameras supported by the source.
        /// </summary>
        /// <returns></returns>
        public IList<string> GetCameras()
        {
            TWFileSystem fs = new TWFileSystem();
            List<string> cams = new List<string>();
            var rc = DGControl.FileSystem.GetFirstFile(fs);
            while (rc == ReturnCode.Success)
            {
                switch (fs.FileType)
                {
                    case FileType.Camera:
                    case FileType.CameraBottom:
                    case FileType.CameraTop:
                    case FileType.CameraPreview:
                        cams.Add(fs.OutputName);
                        break;
                }
                rc = DGControl.FileSystem.GetNextFile(fs);
            }
            return cams;
        }

        /// <summary>
        /// Sets the target camera for cap negotiation that can be set per camera.
        /// </summary>
        /// <param name="cameraName"></param>
        /// <returns></returns>
        public ReturnCode SetCamera(string cameraName)
        {
            TWFileSystem fs = new TWFileSystem();
            fs.InputName = cameraName;
            return DGControl.FileSystem.ChangeDirectory(fs);
        }

        #endregion
    }
}