import java.io.FileNotFoundException;

/**
 * A SEO is a Sequence of Elementary Operators.
 * This class reads a SEO from an English file.
 * It pre-multiplies _matrix by each elementary operation 
 * it reads. _matrix is initially the unit matrix.
 */
 
public class SEO_reader {

    private String      _filePrefix;
    private int         _numBits;
    private boolean     _useSparse;
    private UnitaryMat  _matrix;
    private java.util.Scanner   _englishIn;
    private LineList 		_lineList;
    private int _lenOfSEO;
 

/**
 *
 */
    public SEO_reader(
        String filePrefix, 
        int numBits, 
        boolean useSparse) 
	{
		_filePrefix = filePrefix;
		_numBits = numBits;
		_useSparse = useSparse;
		//constructor sets _matrix to identity
		_matrix = new UnitaryMat(numBits, useSparse);
		try{
			java.io.File    file = 
				new java.io.File(filePrefix + "_eng.txt");
			_englishIn = new java.util.Scanner(file);
			_lineList = new LineList();
			_lenOfSEO = 0;
						
			String lineName = "";
			while(_englishIn.hasNext()){
				lineName = _englishIn.next();
				if(lineName.compareTo("LOOP")==0){
					readLOOP();
				}else if(lineName.compareTo("NEXT")==0){
					readNEXT();
				}else if(lineName.compareTo("SWAP")==0){
					readSWAP();
				}else if(lineName.compareTo("PHAS")==0){
					readPHAS();
				}else if(lineName.compareTo("P0PH")==0){
					readP_PhaseFactor(0);
				}else if(lineName.compareTo("P1PH")==0){
					readP_PhaseFactor(1);
				}else if(lineName.compareTo("SIGX")==0){
					readSIG(1);
				}else if(lineName.compareTo("SIGY")==0){
					readSIG(2);
				}else if(lineName.compareTo("SIGZ")==0){
					readSIG(3);
				}else if(lineName.compareTo("HAD2")==0){
					readHAD2();
				}else if(lineName.compareTo("ROTX")==0){
					readROT(1);
				}else if(lineName.compareTo("ROTY")==0){
					readROT(2);   
				}else if(lineName.compareTo("ROTZ")==0){
					readROT(3);   
				}else if(lineName.compareTo("ROTN")==0){
					readROTN();  
				}else{
					throw new RuntimeException(
						"reading an unsupported LineEnum");                
				}
			}
		}
		catch(FileNotFoundException ex){
			ex.printStackTrace();
		}
		 
 
		int lineListLen = _lineList.size();
		int currentLine = 0;
		while(currentLine < lineListLen){
			Line l = _lineList.get(currentLine);
			switch(l._lineType){
				case 	LOOP:
					//increment timesVisited
					l._ints[1]++;
					break;
				case 	NEXT:
					int loopNum = l._ints[0];
					if(_lineList.get(loopNum)._ints[0]/*reps*/
					      != 
					   _lineList.get(loopNum)._ints[1]/*timesVisited*/
					){
						currentLine = loopNum;
						//increment timesVisited
						_lineList.get(loopNum)._ints[1]++;
					}else{
						//set timesVisited to zero
						_lineList.get(loopNum)._ints[1] = 0;
					}
					break;
				case 	SWAP:
					_lenOfSEO++;
					_matrix.premultiplyByControlledBitSwap(
						l._ints[0],//bit1
						l._ints[1],//bit2
						l._controls);
					break;
				case 	PHAS:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitPhaseFactor(
							l._doubles[0]*Math.PI/180));
					break;
				case 	P0PH:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitP0PhaseFactor(
							l._doubles[0]*Math.PI/180));
					break;
				case 	P1PH:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitP1PhaseFactor(
							l._doubles[0]*Math.PI/180));
					break;
				case 	SIGX:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new SigX());
					break;
				case 	SIGY:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new SigY());
					break;
				case 	SIGZ:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new SigZ());
					break;
				case 	HAD2:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitHadamard());
					break;
				case 	ROTX:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitRot(
							l._doubles[0]*Math.PI/360, 
							OneBitRot.X_AXIS));
					break;
				case 	ROTY:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitRot(
							l._doubles[0]*Math.PI/360, 
							OneBitRot.Y_AXIS));
					break;
				case 	ROTZ:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitRot(
							l._doubles[0]*Math.PI/360, 
							OneBitRot.Z_AXIS));
					break;
				case 	ROTN:
					_lenOfSEO++;
					_matrix.premultiplyByControlledGate(
						l._ints[0],//targetBitPosition
						l._controls,
						new OneBitRot(
							l._doubles[0]*Math.PI/360, 
							l._doubles[1]*Math.PI/360, 
							l._doubles[2]*Math.PI/360));
					break;
				default:
					throw new RuntimeException(
					"reading an unsupported line type");
			}//end cases
			currentLine++;
		}//end while
    }
/**
 *
 */    
    protected void finalize() throws java.lang.Throwable{
        if(_englishIn != null){
            _englishIn.close();
        }
        super.finalize();
    }
/**
 * 
 */

    private Controls readControlInfo(){
    	
    	Controls controls = new Controls();
        int numControls = 0;
        //go to next line and return what was skipped
        String  str = _englishIn.nextLine();
        //tokenize string with one or more white space characters as delimiter
        String[] tokens = (str.trim()).split("[\\s]+");
        if(tokens.length > 0){
        	//first token is the word "IF"
            numControls = (tokens.length - 1);
            controls = new Controls(_numBits, numControls);
            for(int j=0; j<numControls; j++){
                //Control bit positions will be in decreasing order,
                //because that is how the SEO_writer wrote them.
            	int len = tokens[j+1].length();
            	String subString = tokens[j+1].substring(0, len-1);
                controls.setBitPos(j, 
                		Integer.parseInt(subString));
                //last character of token is T or F
                controls.setType(j, 
                		(tokens[j+1].charAt(len-1)=='T'?true:false));
            }
        }
        return controls;
    }
/**
 * 
 */
    private void readLOOP(){
    	int lineNum = _englishIn.nextInt();
    	_englishIn.next();//skip "REPS:"
    	int reps = _englishIn.nextInt();
 		_lineList.addLOOP(lineNum, reps);
    }
/**
 * 
 */
    private void readNEXT(){
    	int loopNum = _englishIn.nextInt();
 		_lineList.addNEXT(loopNum);
    }

/**
 * 
 */
    private void readSWAP(){
    	int bit1 = _englishIn.nextInt();
    	int bit2 = _englishIn.nextInt();
    	Controls controls = readControlInfo();
		_lineList.addSWAP(
    			bit1,
    			bit2,
    			controls);                   	
    }
 /**
  * 
  */   	
    private void readPHAS(){
    	double angleDeg = _englishIn.nextDouble();
    	_englishIn.next();//skip "AT"
    	int targetBitPosition = _englishIn.nextInt();
    	Controls controls = readControlInfo();
    	_lineList.addPHAS(
    			angleDeg,
    			targetBitPosition,
    			controls);                   	
    }
    
/**
 *
 */
   private void readP_PhaseFactor(int projectionBit){
	   double angleDeg = _englishIn.nextDouble();
	   _englishIn.next();//skip "AT"
       int targetBitPosition = _englishIn.nextInt();
       Controls controls = readControlInfo();
       OneBitGate g = null;
       switch(projectionBit){
			case 0:
				g = new OneBitP0PhaseFactor(angleDeg*Math.PI/180);
				break;
			case 1:
				g = new OneBitP1PhaseFactor(angleDeg*Math.PI/180);
				break;
			default:
				Throw.If(true, "unsupported phase shift gate");
       }
       _lineList.addP_PH(
       		projectionBit,
       		angleDeg,
           	targetBitPosition,
           	controls);                   	
   }

/**
 *
 */
    private void readSIG(int direction){
    	_englishIn.next();//skip "AT"
        int targetBitPosition = _englishIn.nextInt();
        Controls controls = readControlInfo();
        OneBitGate g = null;
        switch(direction){
        	case 1:
        		g = new SigX();
        		break;
        	case 2:
        		g = new SigY();
        		break;
        	case 3:
        		g = new SigZ();
        		break;
        	default:
        		Throw.If(true, "unsupported gate direction");
        }
        _lineList.addSIG(
            	direction,
            	targetBitPosition,
            	controls);                   	
    }
/**
 * 
 */    
    private void readHAD2(){
    	_englishIn.next();//skip "AT"
    	int targetBitPosition = _englishIn.nextInt();
    	Controls controls = readControlInfo();
    	_lineList.addHAD2(
    			targetBitPosition,
    			controls);                   	
    }

/**
 *
 */
    private void readROT(int direction){
    	double angleDeg = _englishIn.nextDouble();
        _englishIn.next();//skip "AT"
        int targetBitPosition = _englishIn.nextInt();
        Controls controls = readControlInfo();
        _lineList.addROT(
        		direction,
        		angleDeg,
            	targetBitPosition,
            	controls);                   	
    }
 /**
  * 
  */
    private void readROTN(){
    	double angleX_deg = _englishIn.nextDouble();
    	double angleY_deg = _englishIn.nextDouble();
    	double angleZ_deg = _englishIn.nextDouble();
    	_englishIn.next();//skip "AT"
    	int targetBitPosition = _englishIn.nextInt();
    	Controls controls = readControlInfo();
    	_lineList.addROTN(
    			angleX_deg,
    			angleY_deg,
    			angleZ_deg,
    			targetBitPosition,
    			controls);                   	   

    }
   
/**
 *
 */
    public Complex getEntry(int row, int col) {
    	return _matrix.getEntry(row, col);
    }
/**
 *
 */
	public int getLenOfSEO() {
		return _lenOfSEO;
	}
/**
 *
 */
    public String toString(){
    	
    	return _matrix.toString();
    	
    }
}
