/* JEsd
 * Copyright (C) 1999 JCraft Inc.
 *  
 * Written by: 1999 ymnk
 *   
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package com.jcraft.jesd;

//import javax.media.sound.midi.*;
//import javax.media.sound.sampled.*;
import javax.sound.midi.*;
import javax.sound.sampled.*;

class Client extends Thread {

  // for bounds checking
  static final int ESD_PROTO_MAX=23;

  static final int ESD_NAME_MAX=128;

  static final int ESD_KEY_LEN=16;
  static final int ESD_ENDIAN_KEY=('E' << 24)+('N' << 16)+('D' << 8)+('N');

  java.net.Socket socket;
  IO io;
  boolean swap;
  int request;

  Client(java.net.Socket socket) throws java.io.IOException{
    int reply=1; // yes
    this.socket=socket;
    java.io.InputStream in=socket.getInputStream();
    java.io.OutputStream out=socket.getOutputStream();

    byte[] esd_key = new byte[ESD_KEY_LEN];
    in.read(esd_key, 0, ESD_KEY_LEN);

    if(!Auth.auth(esd_key)){
      reply=0;
    }

    byte[] endian = new byte[4];
    in.read(endian, 0, 4);

    if(endian[0]=='E'){ io=new IOMSB();}
    else{ io=new IOLSB();}

    io.setSocket(socket);

    io.writeInt(reply);
    if(reply==1) add(this);
    if(reply==0){ try{io.close();}catch(Exception e){}; io=null; }
  }

  public void run(){
    if(io==null) return;

    int reqType;
    int format, rate, size, res, version, ok, foo;
    int left_scale, right_scale;

    int id=0, stream_id;
    Sample sample;
    byte[] name=new byte[ESD_NAME_MAX];
    try{
      while (true) {
	reqType=io.readInt();
        //System.out.println("reqType="+Integer.toHexString(reqType));
	switch(reqType){
	case JEsd.ESD_PROTO_STREAM_PLAY:
	  format=io.readInt();
          //System.out.println("format="+Integer.toHexString(format));
	  rate=io.readInt();
          //System.out.println("rate="+rate);
	  io.readByte(name);
          //System.out.println("name="+new String(name));

	  {
	    StreamPlayer player=
	      new StreamPlayer(format, rate, new String(name));
            if(player.outputLine!=null){
	      player.play(io);
            }
            else{
              try{io.close();}catch(Exception e){}
            }
	    del(this);
	    return;
	  }

//	  break;

	case JEsd.ESD_PROTO_STREAM_MON:
	  format=io.readInt();
	  //System.out.println("format="+Integer.toHexString(format));
	  rate=io.readInt();
	  //System.out.println("rate="+rate);
	  io.readByte(name);
	  //System.out.println("name="+new String(name));
	  while(true){
	    io.writeByte(0);
	  }
	case JEsd.ESD_PROTO_SAMPLE_CACHE:
	  format=io.readInt();
	  //System.out.println("format="+Integer.toHexString(format));
	  rate=io.readInt();
	  //System.out.println("rate="+rate);
	  size=io.readInt();
	  //System.out.println("size="+size);
	  io.readByte(name);
	  //System.out.println("name="+new String(name));

	  sample=new Sample(this, format, rate, size, new String(name));
	  io.writeInt(sample.id);
	  sample.fill(io);
	  io.writeInt(sample.id);

	  break;

	case JEsd.ESD_PROTO_SAMPLE_LOOP:
	  foo=io.readInt();
          //System.out.println("sample loop: "+foo);
	  sample=Sample.get(foo);
          if(sample!=null){
	    SamplePlayer player=new SamplePlayer(sample);
	    player.loop();
	  }
          else{foo=0;}
 	  io.writeInt(foo);
	  break;

	case JEsd.ESD_PROTO_SAMPLE_PLAY:
	  foo=io.readInt();
          //System.out.println("sample play: "+foo);
	  sample=Sample.get(foo);
          if(sample!=null){
	    SamplePlayer player=new SamplePlayer(sample);
	    player.play();
	  }
          else{foo=0;}
	  io.writeInt(1);
	  break;

	case JEsd.ESD_PROTO_SAMPLE_STOP:
	  foo=io.readInt();
	  //System.out.println("sample="+foo);
	  sample=Sample.get(foo);
          if(sample!=null && sample.player!=null){
	    sample.player.stop_playing();
	  }
          else{foo=0;}
	  io.writeInt(foo);
	  break;

	case JEsd.ESD_PROTO_SAMPLE_KILL:
	  foo=io.readInt();
	  //System.out.println("sample="+foo);
	  sample=Sample.get(foo);
          if(sample!=null && sample.player!=null){
	    sample.player.kill();
	  }
          else{foo=0;}
	  io.writeInt(foo);
	  break;

	case JEsd.ESD_PROTO_SAMPLE_FREE:
	  foo=io.readInt();
	  //System.out.println("sample="+foo);
	  sample=Sample.get(foo);
	  if(sample!=null)sample.free();
	  io.writeInt(1);
	  break;
        case JEsd.ESD_PROTO_LOCK:
	  res=(Auth.auth(io)?1:0);
	  io.writeInt(res); // ok
	  break;
        case JEsd.ESD_PROTO_UNLOCK:
	  res=(Auth.auth(io)?1:0);
	  io.writeInt(res); // ok
	  break;
        case JEsd.ESD_PROTO_STANDBY:
	  res=(Auth.auth(io)?1:0);
	  io.writeInt(res); // ok
	  break;
        case JEsd.ESD_PROTO_RESUME:
	  res=(Auth.auth(io)?1:0);
	  io.writeInt(res); // ok
	  break;
        case JEsd.ESD_PROTO_SAMPLE_GETID:
	  io.readByte(name);
	  //System.out.println("name="+new String(name));
	  sample=Sample.get(new String(name));
	  id=(sample==null?0:sample.id);
	  io.writeInt(id); // ok
	  break;
        case JEsd.ESD_PROTO_SERVER_INFO:
	  Daemon.protoServerInfo(io);
	  break;
        case JEsd.ESD_PROTO_ALL_INFO:
	  Daemon.protoAllInfo(io);
	  break;

        case JEsd.ESD_PROTO_SAMPLE_PAN:
	  foo=io.readInt();
	  //System.out.println("sample="+foo);
	  left_scale=io.readInt();
	  //System.out.println("left_scale="+left_scale);
	  right_scale=io.readInt();
	  //System.out.println("right_scale="+right_scale);

	  io.writeInt(1); // ok

	  break;
        case JEsd.ESD_PROTO_STREAM_PAN:
	  stream_id=io.readInt();
	  //System.out.println("stream_id="+stream_id);
	  left_scale=io.readInt();
	  //System.out.println("left_scale="+left_scale);
	  right_scale=io.readInt();
	  //System.out.println("right_scale="+right_scale);

	  //Player p=Player.get(stream_id);
	  //System.out.println("player="+p);

	  io.writeInt(1); // ok
	  break;
        case JEsd.ESD_PROTO_STANDBY_MODE:
	  io.writeInt(1/*ESM_ON_STANDBY*/); // ok
	  break;
        case JEsd.ESD_PROTO_LATENCY:
          {
            int amount;
            if((JEsd.esd_audio_format&JEsd.ESD_STEREO)!=0){
              if((JEsd.esd_audio_format&JEsd.ESD_BITS16)!=0)
  	        amount=(44100*(JEsd.ESD_BUF_SIZE+64))/JEsd.esd_audio_rate;
              else
	        amount=(44100*(JEsd.ESD_BUF_SIZE+128))/JEsd.esd_audio_rate;
   	    }
            else {
              if((JEsd.esd_audio_format&JEsd.ESD_BITS16)!=0)
                amount=(2*44100*(JEsd.ESD_BUF_SIZE+128))/JEsd.esd_audio_rate;
              else
	        amount=(2*44100*(JEsd.ESD_BUF_SIZE+256))/JEsd.esd_audio_rate;
  	    }
 	    io.writeInt(amount);
	  }
	  break;
/*
        case JEsd.ESD_PROTO_VORBIS_STREAM:
          {
	  }
	  break;
        case JEsd.ESD_PROTO_VORBIS_SAMPLE:
          {
	  }
	  break;
*/
        default:
	  System.out.println("not-implemented: "+Integer.toHexString(reqType));
	}
      }
    }
    catch (java.io.IOException e) {
      //System.out.println(e);
    }
    del(this);
  }
  static private java.util.Vector pool=new java.util.Vector();
  static synchronized void add(Client client){ pool.addElement(client); }
  static synchronized void del(Client client){ pool.removeElement(client); }
}
