// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2018 Thomas Klose & Torsten Schache, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using Microsoft.Win32;
using System.Reflection;
using GLib;

using Hiperscan.Extensions.ApoIdentCommon;

namespace Hiperscan.Extensions.ApoIdent
{
    public class Scanner
    {
        public event EventHandler ScanDetected = null;
        private const int NO_POLLING = -1;

        public Scanner(Func<bool> scan_condition)
        {
            this.ScanCondition = scan_condition;
            this.Init();
            this.StartPolling();
        }

        private void Init()
        {
            Console.WriteLine("Initialize scanner");


            string path = this.InstallPath;

            if (String.IsNullOrEmpty(path) == true)
            {
                Console.WriteLine("Cannot initialize scanner - no registry entry found.");
                return;
            }

            this.Instance = this.GetInstance(path);

            if (this.Instance == null)
            {
                Console.WriteLine("Cannot initialize scanner - no iscanner instance available.");
                return;
            }

            this.IsInitialized = true;
            Console.WriteLine("Scanner successfully initialized");
            Console.WriteLine("Scanner-Version; {0}", this.Instance.Version);
        }

        private void StartPolling()
        {
            Console.WriteLine("Scanner start polling ....");
            if (this.IsInitialized && this.TimerId == Scanner.NO_POLLING)
            {                
                this.TimerId = (uint)GLib.Timeout.Add(200, this.Scan);
            }
        }

        private void StopPolling()
        {
            Console.WriteLine("Scanner stop polling ....");
            if (this.IsInitialized && this.TimerId != Scanner.NO_POLLING)
            {
                GLib.Source.Remove((uint)this.TimerId);
                this.TimerId = -1;
            }               
        }

        private IScanner GetInstance(string path)
        {
            try
            {
                Assembly assembly = Assembly.LoadFrom(path);
                foreach (Type type in assembly.GetTypes())
                {
                    if (!type.IsPublic || (type.Attributes & TypeAttributes.Abstract) != 0)
                        continue;

                    Type itype = type.GetInterface(typeof(IScanner).FullName);

                    if (itype == null)
                        continue;

                    IScanner iscanner = (IScanner)assembly.CreateInstance(type.FullName);
                    iscanner.Init();
                    return iscanner;
                }

                Console.WriteLine("Scanner: cannot find Interface IScanner");
                return null;

            }
            catch(Exception ex)
            {
                Console.WriteLine("Scanner: Error while creating instance: {0}", ex);
                return null;
            }
        }

        private bool Scan()
        {
            ScannerInfo scan = null;

            if (this.CanScan == true)
            {
                try
                {
                    scan = this.Instance.GetScannerInfo();
                                                       }
                catch
                {
                    scan = new ScannerInfo(ScannerInfoType.Invalid);
                }
                finally
                {
                    if (scan != null)
                    {
                        this.ScannerInfo = scan;

                        this.can_scan = false;
                        if (this.ScanDetected != null)
                            this.ScanDetected(this, EventArgs.Empty);
                    }
                }
            }
                
            return true;
        }

        public void Exit()
        {
            try
            {
                if (this.IsInitialized == false)
                    return;                       

                this.StopPolling();
                this.Instance.Exit();
            }
            catch
            {

            }
        }

        private string InstallPath
        {
            get
            {
                try
                {
                    RegistryKey key = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("HiperScan").OpenSubKey("Scanner");
                    return (string)key.GetValue("InstallPath");
                }
                catch
                {
                    return null;
                }
            }
        }



        private bool can_scan = false;
        private bool CanScan
        {
            get
            {
                if (this.IsInitialized == false)
                {
                    this.can_scan = false;
                    return this.can_scan;
                }

                bool tmp = false;

                if (this.ScanCondition == null)
                    tmp = true;
                else
                        tmp = this.ScanCondition();    

                if (tmp == this.can_scan)
                    return tmp;

                if (this.can_scan == false)
                {
                    try
                    { 
                        Console.WriteLine("Discard scan ...");
                        this.Instance.GetScannerInfo();
                    }
                    catch{}
                }

                this.can_scan = tmp;
                return this.can_scan;
            }
        }

        private bool IsInitialized { get; set; } = false;
        private IScanner Instance { get; set; }
        private long TimerId { get; set; } = Scanner.NO_POLLING;

        public Func<bool> ScanCondition { get; set; }
        public ScannerInfo ScannerInfo { get; set; }
        public ScannerInfo PreviewScanInfo { get; set; }
    }
}
