import java.io.FileNotFoundException;

/**
 * A SEO is a Sequence of Elementary Operators.
 * This class writes an English and Picture file.
 */
public abstract class SEO_writer {
    protected String  _filePrefix;
    protected int _numBits;
    protected int _numBitsInP0Pad;
    protected static int _lineCounter = 0;
    protected java.io.PrintWriter  _englishOut = null;
    protected java.io.PrintWriter  _pictureOut = null;

    
/**
 *
 */
    public SEO_writer(
	   String filePrefix,
	   int numBits
    ){
         _filePrefix = filePrefix;
        Throw.If(numBits<2, "number of bits must be >= 2");
        _numBits = numBits;
        _numBitsInP0Pad = 0; //default

        
		try{
		
			//English File
			java.io.File    engFile = 
				new java.io.File(filePrefix + "_eng.txt");
				_englishOut = new java.io.PrintWriter(engFile);

			//Picture File
			java.io.File    picFile = 
				new java.io.File(filePrefix + "_pic.txt");
				_pictureOut = new java.io.PrintWriter(picFile);
		   
		}
		catch(FileNotFoundException ex){
			ex.printStackTrace();
		}


    }
/**
 * 
 */
    public SEO_writer(
    		java.io.PrintWriter  englishOut,
    		java.io.PrintWriter  pictureOut,
    		int numBits,
    		int numBitsInP0Pad)
    {
    	
    	_englishOut = englishOut;
    	_pictureOut = pictureOut;
    	Throw.If(numBits<2, "number of bits must be >= 2");
    	
        _numBits = numBits;
        _numBitsInP0Pad = numBitsInP0Pad;
     

    }
    
    
    
/**
 * 
 */    
    protected void closeFiles(){
    	_lineCounter = 0;
       
	   if(_englishOut != null){
			_englishOut.close();
			_englishOut = null;
	   }
	   
	   if(_pictureOut != null){
			_pictureOut.close();
			_pictureOut = null;
	   }

    }
/**
 *
 */
    public void writeLOOP(int lineNum, int reps){
    	_lineCounter++;	
		Throw.If(_englishOut==null, "writing to a closed file");
		Throw.If(_pictureOut==null, "writing to a closed file");
		
		_englishOut.println("LOOP\t" + lineNum + "\tREPS:\t" + reps);
		_pictureOut.println("LOOP\t" + lineNum + "\tREPS:\t" + reps);

    }
/**
 *
 */
    public void writeNEXT(int loopNum){
        _lineCounter++;
		Throw.If(_englishOut==null, "writing to a closed file");
		Throw.If(_pictureOut==null, "writing to a closed file");
		
		_englishOut.println("NEXT\t" + loopNum);
	    _pictureOut.println("NEXT\t" + loopNum);

    }
/**
 *
 */   
    public void writeControlledBitSwap(		
    	int bit1, 
    	int bit2,
    	Controls  controls
    ){
    	_lineCounter++;
		Throw.If(_englishOut==null, "writing to a closed file");
		Throw.If(_pictureOut==null, "writing to a closed file");

        Throw.If(bit1 == bit2, "swapped bits must be different");
        Throw.IfOut(bit1, _numBits);
        Throw.IfOut(bit2, _numBits);
        int big = Math.max(bit1, bit2);
        int small = Math.min(bit1, bit2);
        
        controls.addP0_padding( _numBits, _numBitsInP0Pad);
        controls.checkBitPosAreDecreasing();
        
        int numControls = controls.getNumControls();
        int c;
    	
		//English file
		_englishOut.print("SWAP\t" + big + '\t' + small);
		_englishOut.print(numControls!=0?"\tIF\t":'\n');
		//list bit-positions in decreasing order
		for(c=0; c<numControls; c++){
			_englishOut.print(
				controls.getBitPos(c)  +
				(controls.getType(c) == true?"T":"F") + 
				(c==(numControls-1)?'\n':'\t')
				);
		}
        
		
        //picture file
		int biggest = big;
		int smallest = small;
		if(numControls!=0){
			biggest = Math.max(
					controls.getBitPos(0),
					big);
			smallest = Math.min(
					controls.getBitPos(numControls- 1),
					small);
		}

		/*
		for(int k=0; k<_numBitsInPad; k++){
			_pictureOut.print("|   ");
		}
		*/
		
		int j;//a bit position
		for(j=_numBits + _numBitsInP0Pad -1; j>biggest; j--){
			_pictureOut.print("|   ");               
		}
		for(j=biggest ; j>=smallest ; j--){
			boolean     isBig = (j==big);
			boolean     isSmall = (j==small);
			boolean     isControl = false;
			boolean     controlType = false;
			String      tres = (j==smallest?"   ":"---");
			String      dos = (j==smallest?"  ":"--");
			
			for(c=0; c<numControls; c++){
				if(j==controls.getBitPos(c)){
					isControl = true;
					controlType = controls.getType(c);
					break;
				}
			}
			if(isControl){
				_pictureOut.print((controlType?'@':'O') + tres);
			}else{//is not control
				if(isBig){
					_pictureOut.print('<' + tres);
				}else if(isSmall){
					_pictureOut.print('>' + tres);
				}else{
					_pictureOut.print('+' + tres);
				}
			}
		}
		for(j=(smallest-1); j>=0; j--){
			_pictureOut.print("|   ");               
		}
		_pictureOut.print('\n');
		
		//restore controls to original state    
		controls.removeP0_padding( _numBitsInP0Pad);
    }
/**
 *
 */
   public void writeBitSwap(int bit1, int bit2)
   {
	   writeControlledBitSwap(bit1, bit2, 
			   new Controls(_numBits, 0)/*length zero array*/);  	   	   
   }

    
/**
 * ROTX  ang  
 *    represents exp( i*(sigx/2)*(ang*pi/180) )
 */
    public void writeControlledGate(
        int   targetBitPosition,
        Controls   controls,
        OneBitGate oneBitGate
    ){
    	_lineCounter++;
		Throw.If(_englishOut==null, "writing to a closed file");
		Throw.If(_pictureOut==null, "writing to a closed file");
		
        controls.addP0_padding(_numBits, _numBitsInP0Pad);
		controls.checkBitPosAreDecreasing();

        //number of controls may be zero
        int numControls = controls.getNumControls();
        int c;
        for(c=0; c<numControls; c++){
            Throw.If(controls.getBitPos(c) == targetBitPosition,
              "target bit cannot be a control bit");
        }
        
		
        //english file
		if(oneBitGate instanceof OneBitPhaseFactor){
			_englishOut.print("PHAS\t" + 
			((OneBitPhaseFactor)oneBitGate).getAngle()*180/Math.PI);
		}else if(oneBitGate instanceof OneBitP0PhaseFactor){
			_englishOut.print("P0PH\t" + 
			((OneBitP0PhaseFactor)oneBitGate).getAngle()*180/Math.PI);
		}else if(oneBitGate instanceof OneBitP1PhaseFactor){
			_englishOut.print("P1PH\t" + 
			((OneBitP1PhaseFactor)oneBitGate).getAngle()*180/Math.PI);
		}else if(oneBitGate instanceof SigX){
			_englishOut.print("SIGX");
		}else if(oneBitGate instanceof SigY){
			_englishOut.print("SIGY");
		}else if(oneBitGate instanceof SigZ){
			_englishOut.print("SIGZ");
		}else if(oneBitGate instanceof OneBitHadamard){
			_englishOut.print("HAD2");
		}else if(oneBitGate instanceof OneBitRot){
			switch(((OneBitRot)oneBitGate).getAxis()){
				case OneBitRot.X_AXIS:
					_englishOut.print("ROTX\t" +
					((OneBitRot)oneBitGate).getAngleX()*360/Math.PI
					);
					break;
				case OneBitRot.Y_AXIS:
					_englishOut.print("ROTY\t" +
						((OneBitRot)oneBitGate).getAngleY()*360/Math.PI
						);
					break;
				case OneBitRot.Z_AXIS:
					_englishOut.print("ROTZ\t" +
					((OneBitRot)oneBitGate).getAngleZ()*360/Math.PI
					);
					break;
				case OneBitRot.NOT_XYZ:
					 _englishOut.print("ROTN\t" +
					((OneBitRot)oneBitGate).getAngleX()*360/Math.PI
					 + '\t' +
					((OneBitRot)oneBitGate).getAngleY()*360/Math.PI
					 + '\t' +
					((OneBitRot)oneBitGate).getAngleZ()*360/Math.PI
					);
					break;                              
			}

		}else{
			throw new RuntimeException(
			"writing an unsupported" +
			(numControls==0? " OneBitGate": " ControlledGate"));
		}//end of instanceof
		_englishOut.print("\tAT\t" + targetBitPosition + 
			(numControls!=0?"\tIF\t":'\n')
			);
		//list bit-positions in decreasing order
		for(c=0; c<numControls; c++){
			_englishOut.print(
				controls.getBitPos(c)  +
				(controls.getType(c) == true?"T":"F") + 
				(c==(numControls-1)?'\n':'\t')
				);
		}


        //picture file
		//numBits always >=1, checked in constructor
		int biggest = targetBitPosition;
		int smallest = targetBitPosition;
		if(numControls!=0){
			biggest = Math.max(controls.getBitPos(0),
			targetBitPosition);
			smallest = Math.min(controls.getBitPos(numControls- 1),
			targetBitPosition);
		}
		
		/*
		for(int k=0; k<_numBitsInPad; k++){
			_pictureOut.print("|   ");
		}
		*/
		
		int j;//a bit position
		for(j=_numBits + _numBitsInP0Pad-1; j>biggest; j--){
			_pictureOut.print("|   ");               
		}
		for(j=biggest ; j>=smallest ; j--){
			boolean     isTarget = (j==targetBitPosition);
			boolean     isControl = false;
			boolean     controlType = false;
			String      tres = (j==smallest?"   ":"---");
			String      dos = (j==smallest?"  ":"--");
			
			for(c=0; c<numControls; c++){
				if(j==controls.getBitPos(c)){
					isControl = true;
					controlType = controls.getType(c);
					break;
				}
			}
			if(isControl){
				_pictureOut.print((controlType?'@':'O') + tres);
			}else{//is not control
				if(!isTarget){//is not control nor target
					_pictureOut.print('+' + tres);
				}else{//is target
					if(oneBitGate instanceof OneBitPhaseFactor){
						_pictureOut.print("Ph" + dos);
					}else if(oneBitGate instanceof OneBitP0PhaseFactor){
						_pictureOut.print("OP" + dos);
					}else if(oneBitGate instanceof OneBitP1PhaseFactor){
						_pictureOut.print("@P" + dos);
					}else if(oneBitGate instanceof SigX){
						_pictureOut.print('X' + tres);
					}else if(oneBitGate instanceof SigY){
						_pictureOut.print('Y' + tres);
					}else if(oneBitGate instanceof SigZ){
						_pictureOut.print('Z' + tres);
					}else if(oneBitGate instanceof OneBitHadamard){
						_pictureOut.print('H' + tres);
					}else if(oneBitGate instanceof OneBitRot){
						switch(((OneBitRot)oneBitGate).getAxis()){
							case OneBitRot.X_AXIS:
							  _pictureOut.print("Rx" + dos);
							  break;
							case OneBitRot.Y_AXIS:
							  _pictureOut.print("Ry" + dos);
							  break;
							case OneBitRot.Z_AXIS:
							  _pictureOut.print("Rz" + dos);
							  break;
							case OneBitRot.NOT_XYZ:
							  _pictureOut.print('R' + tres);
							  break;                                  
						}
					}else{
						throw new RuntimeException(
						"writing an unsupported" +
						(numControls==0? " OneBitGate": " ControlledGate"));
					}//end of instanceof
				}
			}
		}
		for(j=(smallest-1); j>=0; j--){
			_pictureOut.print("|   ");               
		}
		_pictureOut.print('\n');
           
		//restore controls to original state
		controls.removeP0_padding( _numBitsInP0Pad);
    }
/**
 *
 */   
    public void writeOneBitGate(
        int   targetBitPosition,
        OneBitGate oneBitGate 
    ){
        writeControlledGate(
        targetBitPosition,
        new Controls(_numBits, 0), //0 controls
        oneBitGate);
    }
/**
 * PHAS angle*180/PI 
 *		represents e(i*angle)
 */   
    public void writeGlobalPhaseFactor(double angle){
        writeControlledGate(
        0, // targetBitPosition, anyone will do
        new Controls(_numBits, 0), //0 controls
        new OneBitPhaseFactor(angle));
    }
/**
 * 
 */
    public int getLineCount(){
    	return _lineCounter;
    }

    
}
