/*
 * Filename: WinRequester.java
 *
 * Author: Joe Wang
 *
 * Copyright (c) 2003-2005 
 * The Trustees of Columbia University in the City of New York
 * Columbia University Information Technology
 *
 * This file is part of NINJa.
 *
 * NINJa 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 2 of the License, or
 * (at your option) any later version.
 *
 * NINJa 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 NINJa; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

package edu.columbia.print.ninja.prix.requester;

import edu.columbia.print.*;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.*;
import com.millipad.util.*;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;

/**
 * Requester for windows machines.  Produces requests from
 * the spool director of a Windows machine.  Generates the 
 * Doc InputStream from the SPL file and the attributes from
 * the SHD file.
 * @author Joe Wang <jw168@columbia.edu>
 * @since 1.0
 */
public class WinRequester extends Requester {

	ResourceBundle bundle;
	OutputDeviceAssigned inputDevice;
	String jobFile;
	FileInputStream jobIS;
        Requester postscript;

	public WinRequester() throws Exception {
		bundle = ResourceBundle.getBundle(getClass().getPackage().getName()+".resource");

		inputDevice = new OutputDeviceAssigned(bundle.getString("WinRequester.input"), null);

		AttributeSet attr = new HashPrintServiceAttributeSet();
		jobFile = bundle.getString("WinRequester.file");
                postscript = new PostscriptRequester();
		}

	void debug(Object in) {Debug.print(WinRequester.class, in);}

            public void setInputDevice(String device) {
    }

	public void addPrintService(net.jini.print.service.PrintService service) {
                postscript.addPrintService(service);
		}

	synchronized void waitTillComplete(File file) throws IOException {
		long size = 0;
		for(;!file.exists();) {
			try {wait(100);}
			catch(Exception e) {continue;}
			}
		RandomAccessFile raf = new RandomAccessFile(file, "r");
		FileChannel channel = raf.getChannel();
		for(;;) {
			try {
				wait(100);
				long newSize = channel.size();
				if(newSize!=size) break;
				}
			catch(Exception e) {
                            debug(e+"");
                            continue;}
			}
		raf.close();
		}

	public int produceRequest(File inFile, PrintRequestAttributeSet attr, Class[] required, BlockingStack result) throws IOException {
		try {
                        int index = inFile.getName().indexOf("FP");
			if(index==0) return REQUEST_FAILED;
			index = inFile.getName().indexOf(".SPL");
			if(index<0) return REQUEST_FAILED;
			String name = inFile.getName().substring(0, index);
			name = inFile.getParentFile().getPath() + File.separator + name;
			waitTillComplete(inFile);
	
			//get the attributes for the job
			File shd = new File(name+".SHD");
                        if(!shd.exists()) {
//                            inFile.delete();
  //                          result.push(inFile);
                            return REQUEST_FAILED;
                        }
			AttributeSet in = parse(shd);
			TextSyntax user = (TextSyntax )in.get(JobOriginatingUserName.class);
			if(!inputDevice.equals(in.get(OutputDeviceAssigned.class))
                        && user.getValue().indexOf("@\\")>=0) return REQUEST_FAILED;
	
			PrintRequestAttributeSet out = new HashPrintRequestAttributeSet();
			if(user!=null) out.add(new RequestingUserName(user.getValue(), null));
			in.remove(JobOriginatingUserName.class);
			in.remove(OutputDeviceAssigned.class);
			out.addAll(in);
                        attr.addAll(out);

                        int retVal = postscript.produceRequest(inFile, attr, required, result);
//                        inFile.delete();
//                        shd.delete();
//                        result.push(inFile);
                        result.push(shd);
                        if(retVal != REQUEST_DISPATCHED) {
                            debug("requestFailed");
                            return REQUEST_FAILED;
                        }
                        //only for lprm
                        //if(!attr.containsKey(Destination.class)) return REQUEST_FAILED;
                        return REQUEST_DISPATCHED;
			}
		catch(Exception e) {
			return REQUEST_FAILED;
			}
		}

	AttributeSet parse(File in) throws IOException {
		RandomAccessFile file = new RandomAccessFile(in, "r");
		AttributeSet set = new HashAttributeSet();
		try {
			file.seek(88);		//number of pages
			byte l = file.readByte();
			byte h = file.readByte();
			int pages = ((h<<8)&0x0ff00)+(l&0x0ff);
			if(pages!=0) set.add(new JobMediaSheets(pages));
			file.seek(20);		//offset to first string
			l = file.readByte();
			h = file.readByte();
			file.seek(((h<<8)&0x0ff00)+(l&0x0ff));
			Vector strings = new Vector();
			for(;;) {
				String tmp = readString(file);
				if(tmp==null) break;
                                //debug("parse "+tmp);
				strings.add(tmp);
				}
			switch(strings.size()) {
			case 8:
				//user@0
				//requestingUser@1
				set.add(new JobName(strings.get(2)+"", null));
				set.add(new OutputDeviceAssigned(strings.get(3)+"", null));
				//driver@4
				//processor@5
				//outputtype@6
                                String string6 = (String )strings.get(6);
                                if(string6.indexOf("job=lpd")==0) set.add(new JobOriginatingUserName(strings.get(0)+"@"+string6.substring(7), null));
				//source@7
                                else set.add(new JobOriginatingUserName(strings.get(0)+"@"+strings.get(7), null));
				break;
			case 9:
				//user@0
				//requestingUser@1
				set.add(new JobName(strings.get(2)+"", null));
				//port@3
				set.add(new OutputDeviceAssigned(strings.get(4)+"", null));
				//driver@5
				//processor@6
				//outputtype@7
                                String string7 = (String )strings.get(7);
                                if(string7.indexOf("job=lpd")==0) set.add(new JobOriginatingUserName(strings.get(0)+"@"+string7.substring(7), null));
				//source@8
                                else set.add(new JobOriginatingUserName(strings.get(0)+"@"+strings.get(8), null));
				break;
			default:
				debug("Unknown SHD file "+in.getName());
				debug(strings.size()+" size");
				}
			}
                catch(IOException e) {
                    System.out.println("error:"+in.getCanonicalPath());
			e.printStackTrace();
                        throw e;
                }
		catch(Exception e) {
			e.printStackTrace();
			}
		finally {
			file.close();
			}
		return set;
		}

	String readString(RandomAccessFile file) throws IOException {
		byte[] buffer = {0,0};
		StringBuffer result = new StringBuffer();
		byte[] tmp = {0};
		for(;;) {
			int count = file.read(buffer);
			if(count<2) break;
			if(buffer[0]==0) break;
			tmp[0] = buffer[0];
			result.append(new String(tmp));
			}
		if(result.length()==0) return null;
		return result.toString();
		}

	}
