package com.synthbot.audioio.vst;

import com.javadaw.audio.writeAudio;
import com.javadaw.gui.javaMain;
import com.synthbot.audioplugin.vst.vst2.AbstractJVstHostListener;
import com.synthbot.jasiohost.AsioChannel;
import com.synthbot.jasiohost.AsioDriver;
import com.synthbot.jasiohost.AsioDriverListener;
import com.synthbot.jasiohost.AsioDriverState;
import java.util.HashSet;
import java.util.Set;
import com.synthbot.minihost.loadVST;

import java.io.File;

import java.math.BigDecimal;

public class asioChannel extends AbstractJVstHostListener implements AsioDriverListener   {

  private Boolean a = true;       
  public AsioDriver asioDriver;
  public Set<AsioChannel> activeChannels;
  private javaMain parent;
  public boolean[] mute;
  public float[] fvolume;
  public float[] fpan;
  private writeAudio writeaudio;  
  public boolean misRunning = false; 
  public int drumsoffset = 0;
  public int metronomeoffset = 0;
  private loadVST lv;
  private runFX runfx;

 
  public asioChannel(javaMain parent) {

    this.parent       = parent;
    this.mute         = new boolean[parent.max_tracks];
    this.fvolume      = new float[parent.max_tracks];
    this.fpan         = new float[parent.max_tracks];
    
    this.writeaudio   = new writeAudio(parent);
    
    activeChannels    = new HashSet<AsioChannel>();

    final AsioDriverListener host = this;
    
    if (!parent.asioinit) {

         asioDriver = AsioDriver.getDriver(parent.audioenginepanel.asioDriver.getItemAt(parent.asiodriverindex).toString());
         //asioDriver = AsioDriver.getDriver("ASIO4ALL v2");
         //asioDriver = AsioDriver.getDriver("E-MU ASIO");
         asioDriver.addAsioDriverListener(host);
         parent.bufferSize = asioDriver.getBufferPreferredSize();
         parent.toolbar.audiotype .setText("  " + String.valueOf(parent.audiotype));
         parent.toolbar.buffersize.setText("  " + String.valueOf(parent.bufferSize));
         activeChannels.add(asioDriver.getChannelOutput(0));
         activeChannels.add(asioDriver.getChannelOutput(1));

         try {
              activeChannels.add(asioDriver.getChannelInput (0));
              activeChannels.add(asioDriver.getChannelInput (1));
              parent.maxaudioinputs = 2;
         } catch (Throwable t) { System.out.println("not present 0&1"); }
         try {
              activeChannels.add(asioDriver.getChannelInput (2));
              activeChannels.add(asioDriver.getChannelInput (3));
              parent.maxaudioinputs = 4;
         } catch (Throwable t) { System.out.println("not present 2&3"); }
         try {
              activeChannels.add(asioDriver.getChannelInput (4));
              activeChannels.add(asioDriver.getChannelInput (5));
              parent.maxaudioinputs = 6;
         } catch (Throwable t) { System.out.println("not present 4&5"); }
         try {
              activeChannels.add(asioDriver.getChannelInput (6));
              activeChannels.add(asioDriver.getChannelInput (7));
              parent.maxaudioinputs = 8;
         } catch (Throwable t) { System.out.println("not present 6&7"); }
         
         asioDriver.createBuffers(activeChannels);
         asioDriver.start();
     
         this.parent.fOutputs  = new float[2]                    [this.parent.bufferSize];
         this.parent.fInputs   = new float[parent.maxaudioinputs][this.parent.bufferSize];
         this.parent.ffOutputs = new float[parent.max_tracks][32][this.parent.bufferSize];
     
         parent.asioinit = true;
         
         init();
       
         System.out.println("--ASIO STARTED: " + "--" + parent.audioenginepanel.asioDriver.getItemAt(parent.asiodriverindex).toString()+ "--");
         
    }
  }
  public void startMetronome() {
      misRunning = true;
      metronomeoffset = 0;
  }
  public void initAudio() {
      writeaudio.init(44100*16*parent.selectOf/800);
    }
  public void startExport(File outputFile) {
      writeaudio.setFileName(outputFile.toString());
      writeaudio.offset = 0; 
      parent.isExporting = true;
  }
  public void stopExport() {
      parent.isExporting = false;
      writeaudio.closeFileFromArray();        
  }
  public void init() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           mute[i]                              = false;
           fvolume[i]                           = 1.0F;
           fpan[i]                              = 0.0F;
      }
  }
  public void reset1() {
      
      for (int i = 0; i <= parent.max_channels; i++) { 
           for (int channel = 0; channel < 2; ++channel) {
                for (int sample = 0; sample < parent.bufferSize; ++sample) {
                     parent.fOutputs[channel][sample] = 0.0F;
                     parent.level[i][channel]         = 0.0F;
                     parent.masterlevel[0][channel]   = 0.0F; 
                }
           }
      }
      for (int i = 0; i < parent.maxaudioinputs; i++) { 
           parent.inputlevel[i] = 0.0F;
      }
  }
  public void reset2(int y) {
        
      for (int channel = 0; channel < 2; ++channel) {
           for (int sample = 0; sample < parent.bufferSize; ++sample) {
                       parent.ffOutputs[y][channel][sample] = 0.0F;
           }
      }
  }
  public void setOffset() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           
           if (parent.isRecording) parent.offset1[i] += parent.bufferSize;
           else                    parent.offset2[i] += parent.bufferSize;
          
           metronomeoffset += parent.bufferSize;
           drumsoffset     += parent.bufferSize;
           
          if (i == 0) { if (parent.isRecording) { parent.toolbar.framePos  .setText(String.valueOf(parent.offset1[i])); }
                        else                    { parent.toolbar.framePos  .setText(String.valueOf(parent.offset2[i])); 
                                                  parent.toolbar.tempoInSec.setText(String.valueOf(parent.offset2[i]/44100)); 
                        }
          }
      }
  }
  public void clearOffset1() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           parent.offset1[i] = 0;
           metronomeoffset   = 0;
           drumsoffset       = 0;
      }
  }
  public void clearOffset2() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           parent.offset2[i] = 0;
      }
  }
  public void setOffset1() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           parent.offset1[i] = (int)(parent.seqr.getTickPosition()*220.5f);
      }
      parent.toolbar.framePos.setText("    " + String.valueOf(parent.offset1[0]));
  }
  public void setOffset2() {
      for (int i = 0; i <= parent.max_channels; i++) { 
           parent.offset2[i] = (int)(parent.seqr.getTickPosition()*220.5f);
      }
  }
  public void setVolume(int i, float value) {
      fvolume[i] = value;
  }
  public void setBalance(int i, float value) {
      this.fpan[i] = value;
  }
  public void setMute(int i, boolean bmute) {
      mute[i] = bmute;
  }

  public void bufferSwitch(long systemTime, long samplePosition, Set<AsioChannel> channels) {
      
      float pan = 0.0f;
      
      try { reset1();
            
            for (AsioChannel channelInfo : channels) {
                 for (int i = 0; i < parent.maxaudioinputs; i++) {
                      if (channelInfo.getChannelIndex() == i && channelInfo.isInput()) channelInfo.read(parent.fInputs[i]);
                 }
            }
            if (parent.audioinisopen)
                for (int channel = 0; channel < parent.maxaudioinputs; ++channel) {
                     for (int sample = 0; sample < parent.bufferSize; ++sample) {
                          parent.inputlevel[channel] = Math.max(parent.inputlevel[channel], parent.fInputs[channel][sample]);               
                     }
                }
            
            for (int track = 0; track <= parent.max_channels; ++track) {
                 //if (parent.pluginisloaded[track]) lv = parent.pluginMap.get(String.valueOf(parent.vstplugintochannel[track]));
                 //if (parent.pluginisloaded[track]) lv.vst.processReplacing(parent.ffOutputs[track], parent.ffOutputs[track], parent.bufferSize);
                 if (parent.isLoadedVST[track] && !parent.isLoadedAudio[track]) a = true;
                 else a = false;
                 if (parent.isRunning[track]) synchronized(a?parent.vstMap.get(String.valueOf(track)):parent.audioMap.get(String.valueOf(track)))   {
                     if ( a && !parent.isRerouted[track]) parent.vstMap.  get(String.valueOf(track)).notify();
                     if (!a && !parent.isRerouted[track]) parent.audioMap.get(String.valueOf(track)).notify();
                     parent.mixas.mix(track);
                 }
                 if (!parent.pause && track == 0) setOffset();
            }
            if (parent.fxpluginisloaded[parent.max_tracks-1][0] && parent.bswitchfx[parent.max_tracks-1][0] == 1) {
                lv = parent.pluginfxMap.get(String.valueOf(parent.fxplugintochannel[parent.max_tracks-1][0]));
                lv.vst.processReplacing(parent.fOutputs, parent.fOutputs, parent.bufferSize);
            }
            if (parent.fxpluginisloaded[parent.max_tracks-1][1] && parent.bswitchfx[parent.max_tracks-1][1] == 1) {
                lv = parent.pluginfxMap.get(String.valueOf(parent.fxplugintochannel[parent.max_tracks-1][1]));
                lv.vst.processReplacing(parent.fOutputs, parent.fOutputs, parent.bufferSize);
            }
            parent.mixas.write();
            if (parent.isExporting && parent.exportwav) writeaudio.putSamples(doubleArrayToByteArray(parent.fOutputs, 2, 2));
      } catch (Throwable t) { System.out.println("run asio " + t); }
  }
  public void bufferSizeChanged(int bufferSize) {
  }
  public void latenciesChanged(int inputLatency, int outputLatency) {
  }
  public void resetRequest() {
      new Thread() {
        @Override
        public void run() {
          asioDriver.returnToState(AsioDriverState.INITIALIZED);
        }
      }.start();
  }
  public void resyncRequest() {
  }
  public void sampleRateDidChange(double sampleRate) {
  }
  public byte[] doubleArrayToByteArray(float[][] doubleArray,
                                            int bytesPerSample, int channels) {
        double maxSample;
        int lengthInSamples = doubleArray[0].length;

        if (bytesPerSample == 2)
            maxSample = 32768.0D;
        else if (bytesPerSample == 1)
            maxSample = 128.0D;
        else if (bytesPerSample == 3)
            maxSample = 8388608.0D;
        else if (bytesPerSample == 4) {
            maxSample = 1.47483648E+017D;
        } else {
            maxSample = 0.0D;
        }

        double maxDoubleValuedSample = (maxSample - 2.0D) / maxSample;

        byte[] byteArray =
            new byte[lengthInSamples * bytesPerSample * channels];
        byte[] b = new byte[bytesPerSample];
        int l = 0;
        for (int currSamp = 0; currSamp < lengthInSamples; ++currSamp) {
            for (int currChannel = 0; currChannel < channels; ++currChannel) {
                if (doubleArray[currChannel][currSamp] >=
                    maxDoubleValuedSample)
                    l = (int)maxSample - 2;
                else if (doubleArray[currChannel][currSamp] <=
                         -maxDoubleValuedSample)
                    l = (int)(-maxSample) + 2;
                else
                    l = (int)(doubleArray[currChannel][currSamp] * maxSample);

                for (int i = 0; i < bytesPerSample; ) {
                     b[(bytesPerSample - i - 1)] = (byte)l;

                     i += 1;
                     l >>= 8;
                }
                for (int i = 0; i < bytesPerSample; i += 1)
                     byteArray[(currSamp * bytesPerSample * channels +
                                bytesPerSample * currChannel + i)] = b[i];
             }
        }
        return byteArray;
  }
}

