// 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 System.IO;
using System.Reflection;
using Hiperscan.Common;
using Hiperscan.Extensions.ApoIdentCommon.Scanner;
using Hiperscan.Unix;

namespace Hiperscan.Extensions.ApoIdent
{
    public class Scanner : Polling
    {
        private static readonly string PPN_LOADING_ERROR = Catalog.GetString("The ppn list in the scanner lib contains errors.");

        public event EventHandler ScanDetected = null;

        public Scanner()
        {
            this.PollingTimeIntervall = 500;
            this.ExecutePoolingEventAsyncronously = true;
        }

        public void Init(byte[] ppn_data, bool is_apo_ident_1)
        {
            if (is_apo_ident_1 == true)
                return;

            try
            {
                Console.WriteLine("Initialize scanner");

                string path = this.InstallPath;

                if (ppn_data == null)
                {
                    Console.WriteLine("Cannot initialize scanner - no pzn data found.");
                    return;
                }

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

                this.Instance = this.GetInstance(path, ppn_data);

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

                if (this.Instance.HasPpnData == false)
                {
                    Console.WriteLine(Scanner.PPN_LOADING_ERROR);
                    return;
                }


                Console.WriteLine("Scanner successfully initialized");
                Console.WriteLine("Scanner-Version; {0}", this.Instance.Version);
                this.CanPolling = true;
                return;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot initialize scanner: {0}", ex.Message);
                return;
            }
        }

        private IScanner GetInstance(string path, byte[] pzn_data)
        {
            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(pzn_data);
                    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 string InstallPath
        {
            get
            {
                try
                {
                    string path = Assembly.GetCallingAssembly().Location;
                    path = Path.GetDirectoryName(path);
                    path = Path.Combine(path, "ScannerLib.dll");

                    if (File.Exists(path))
                        return path;

                    return null;
                }
                catch
                {
                    return null;
                }
            }
        }

        protected override void BeforeStartRunning()
        {
            Console.WriteLine("Scanner start polling...");
            try
            {
                this.Instance.GetScannerInfo();
            }
            catch
            {
            }
        }

        protected override void ExecutePollingEvent()
        {
            ScannerInfo scan = null;
            try
            {
                scan = this.Instance.GetScannerInfo();
            }
            catch
            {
                scan = new ScannerInfo();
            }
            finally
            {
                if (scan != null)
                {
                    this.CycleIsRunning = true;
                    this.ScannerInfo = scan;
                    this.ScanDetected?.Invoke(scan, EventArgs.Empty);
                }
            }
        }

        protected override void AfterFinishRunning()
        {
            Console.WriteLine("Scanner polling stopped.");
        }


        public void EmitScan(string raw_data, bool capslock_active)
        {
            try
            {
                if (this.CanPolling)
                {
                    this.Instance.EmitScan(raw_data, capslock_active);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Scanner: Error while emtting scan: {0}", ex);
            }
        }

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

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

            }
        }

        private IScanner Instance { get; set; }

        public ScannerInfo ScannerInfo { get; set; }
        public ScannerInfo PreviewScanInfo { get; set; }
        public bool Activated { private get; set; }
    }
}
