//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using
using Framefield.Core;
using System.Diagnostics;
using System.IO;
using Un4seen.Bass;
using Un4seen.Bass.Misc;
using System.Runtime.InteropServices;

/*  
    v.01
    Michael Auerswald <michael@908video.de>

    Reads live audio through the BASS.Net library.
    
    Outputchannels:
    0: Volume Left+Right Combined (Averaged)
    1: Left
    2: Right

    The Volume is being output on context.Value.

    contex.Dynamic contains a float[512] with FFT data tha can be send to a BassFFT 
    operater to get averages for specific kHz Ranges.
*/

namespace Framefield.Core.ID899a57f9_0878_4c82_a4d4_04e1103194a8
{
    public class Class_AnalyzeAudioFFT : OperatorPart.Function, Framefield.Core.OperatorPartTraits.ITimeAccessor
    {
        //>>> _inputids
        private enum InputId
        {
            InputDeviceId = 0,
            DoRecord = 1,
            OutputChannel = 2,
            SampleFrequencykHz = 3
        }
        //<<< _inputids 

        //>>> _outputids
        private enum OutputId
        {
            Output = 0,
            Volume = 1
        }
        //<<< _outputids
        
        public override void Dispose() {
            StopPlaying();
        }

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) {
            //>>> _params
            var InputDeviceId = inputs[(int)InputId.InputDeviceId].Eval(context).Value;
            var DoRecord = (int) inputs[(int)InputId.DoRecord].Eval(context).Value;
            var OutputChannel = inputs[(int)InputId.OutputChannel].Eval(context).Value;
            var SampleFrequencykHz = inputs[(int)InputId.SampleFrequencykHz].Eval(context).Value;
            //<<< _params

            if (!IsSoundInitialized()) {
                return context;
            }

            if (_lastInputDeviceId!=InputDeviceId)
            {
                _lastInputDeviceId = InputDeviceId;
                var info = Bass.BASS_RecordGetDeviceInfo((int)InputDeviceId);
                if (info!=null && info.IsEnabled==true)
                {
                    Console.WriteLine("Using Input Device: " + info.ToString());
                    _recordDevice = (int)InputDeviceId;
                }
                else
                {
                    Console.WriteLine("Device " + ((int)InputDeviceId).ToString() + " is not enabled");
                    _recordDevice = -1;
                }
                DoRecord = 0;
            }

            // if we are not recording just send zeroes
            if (DoRecord==0) {
                StopPlaying();
                context.Value = 0f;
                context.Dynamic = new float[_SAMPLES];
                _lastDoRecord = DoRecord;
                return context;
            }

            // only start recording when we switch it on, not on every step
            if (DoRecord==1 && _lastDoRecord!=DoRecord)
            {
                StopPlaying(); // cleans up previous objects and streams
                _lastDoRecord = DoRecord;
                _recordDevice = (int)InputDeviceId;
                _myRecProc = new RECORDPROC(MyRecording);
                Bass.BASS_RecordInit(_recordDevice);
                _recHandle = Bass.BASS_RecordStart((int)SampleFrequencykHz*1000, 2, BASSFlag.BASS_RECORD_PAUSE, _myRecProc, IntPtr.Zero);
                Bass.BASS_ChannelPlay(_recHandle, false);
                Logger.Debug(this,"recording...");
            }

            // switch between combined, left and right volume for the context.Value output
            switch ((int)OutputChannel)
            {
                case 0: context.Value = (_left + _right) / 2f / 32768f; break;
                case 1: context.Value = _left / 32768f; break;
                case 2: context.Value = _right / 32768f; break;
            }
            
            // write the current recbuffer to the Dynamic output, contains a float[256] with FFT data
            context.Dynamic = _recbuffer;
            return context;
        }
        
        private void StopPlaying() {
            if (_recHandle != 0) {
                Bass.BASS_ChannelPause(_recHandle);
                Bass.BASS_ChannelStop(_recHandle);
                Bass.BASS_StreamFree(_recHandle);
            }
        }

        private bool IsSoundInitialized() {
            return Bass.BASS_GetInfo() != null;
        }
        
        private bool IsPlaying() {
            return Bass.BASS_ChannelIsActive(_recHandle) == Un4seen.Bass.BASSActive.BASS_ACTIVE_PLAYING;
        }

        private void GetInputDevices() {
            Console.WriteLine("Audio Input Devices");
            for (int i = 0; i < Bass.BASS_RecordGetDeviceCount(); i++)
            {
                Console.WriteLine(i.ToString() + ": " + Bass.BASS_RecordGetDeviceInfo(i));
            }
        }

        private bool MyRecording(int handle, IntPtr buffer, int length, IntPtr user)
        {
            _recbuffer = new float[_SAMPLES];

            // Handle
            _recHandle = handle;
            GCHandle hGC = GCHandle.Alloc(_recbuffer, GCHandleType.Pinned);
         
            // Read data
            Bass.BASS_ChannelGetData(handle, hGC.AddrOfPinnedObject(), (int)BASSData.BASS_DATA_FFT1024);

            // Calculate volume levels
            _level = (int)(Bass.BASS_ChannelGetLevel(handle));
            _left = (int)(Utils.LowWord32(_level)); // the left level 
            _right = (int)(Utils.HighWord32(_level)); // the right level
            return true;
        }

        // Globals
        const int _SAMPLES = 512; // hardcoded number of fft samples
        BASS_CHANNELINFO _channelInfo;
        float _lastInputDeviceId;
        float _lastDoRecord;
        int _recordDevice;
        RECORDPROC _myRecProc;
        float[] _recbuffer = new float[_SAMPLES];
        int _recHandle;
        int _left, _right, _level;
    }
}

