using System;
using System.IO;
using System.Text;
using Excel;
using System.Reflection;

namespace CompareXls
{
 /// <summary>
 /// Diff の概要の説明です。
 ///
 /// Change Java to C# by xzl@DCE at 2004/05
 ///                                    zl_xue@hotmail.com
 /// @Version C# version 0.1  2004
 /// </summary>
 
 /// <summary>
 /// This is the info kept per-file.
 /// </summary>
 class fileInfo
 {

  internal static int MAXLINECOUNT = 200;
  public int maxLine;  // After input done, # lines in file.
  internal int[] other; // Map of line# to line# in other file.
  internal node[] symbol; // The symtab handle of each file.
        // (-1 means do not know.)
        // Allocated after the lines are read.

//  internal System.IO.StreamReader  file;  ----->delete
  
  // Excel Application to Open Excel Files. --->add
  private Excel.Application xlsApp;
  private Excel.Workbook xlsBook;
  private Excel.Sheets xlsSheets;
  internal Excel._Worksheet  xlsSheet;


  /// <summary>
  /// Normal constructor with one filename: fileis opened and saved.
  /// </summary>
  /// <param name="fileName">File name</param>
  internal fileInfo(string fileName)
  {
   symbol = new node[MAXLINECOUNT+2];
   other = null; //allocated later.
   try
   {
  
    // ***********File Path Test***********
    fileName=@"G:\test\CompareXls\bin\Debug\"+fileName;
   
    xlsApp= new Excel.Application();
    xlsApp.Visible =false;
    try{
     xlsBook= xlsApp.Workbooks.Open(fileName,
      Missing.Value,false,Missing.Value,null,null,
      Missing.Value,Missing.Value,Missing.Value,
      Missing.Value,Missing.Value,Missing.Value,
      Missing.Value,Missing.Value,Missing.Value);
     xlsSheets=xlsApp.Sheets;
     xlsSheet=(Worksheet)xlsSheets.get_Item(1);
    }
    catch{
     xlsClose();
     return;
    }
    
//    file = new System.IO.StreamReader(fileName);  ----->delete
   }
   catch(System.NullReferenceException e){
    Console.WriteLine ("File Error! "+e.ToString());
    //--->add
    xlsClose();
    return;   
   }
  }

  //This is done late, to be same size as # lines in input file.
  internal void alloc()
  {
   other = new int[symbol.Length +2];
  }

  //Close excel file  --->add
  internal void xlsClose(){
   xlsApp.Quit();
   xlsSheets = null ;
   xlsBook = null ;
   xlsApp = null ;
   GC.Collect() ;
  }
 }

 /// <summary>
 /// Diff  Text file difference utility.
 /// </summary>
 class Diff
 {
  /// <summary>
  /// アプリケーションのメイン エントリ ポイントです。
  /// </summary>
  ///
  
  // block len > and Psssible real block len.
  int unReal=int.MaxValue ;

  // Keeps track of information abouot file1 and file2.
  fileInfo oldInfo, newInfo;

  /* blocklen is the info about blocks. It will be set to 0, except at the line#s where
   * blocks start in the old file. At these plcaes it will be set to the # lines in the
   * block. During printout, this # will be reset to -1 if the block is printed as a MOVE
   * block (because the printout phase will encouter the block twice, but must only print
   * it once .) .
   * The array declarations are to MAXLINECOUNT+2 so that we can have two extra lines at
   * line# 0 and line# MAXLINECOUNT+1 (or less).
   */
  int[] blocklen;

  [STAThread]
  static void Main(string[] args)
  {
   //
   // TODO: アプリケーションを開始するコードをここに追加してください。
   //

   Console.WriteLine("Input Compare File --| oldFile(*.xls) newFile(*.xls) |--> ");
   // Read File Name
   string strRead=Console.ReadLine ();
   
   if(strRead!=string.Empty)
   {
    string[] strFileName=strRead.Split( );
    Diff d= new Diff ();
    d.doDiff(strFileName[0],strFileName[1]);
    //--->add
    Console.WriteLine("Enter Exit.");
    Console.Read();
   }
   else
   {
    Console.WriteLine("Useage: oldFile(*.xls) newFile(*.xls)");
   }
   return;
  }

  //Construct a Diff object.
  Diff(){
  }

  /// <summary>
  /// Do one file comparison. Called with both filenames.
  /// </summary>
  /// <param name="oldFile">Old File Name</param>
  /// <param name="newFile">New File Name</param>
  public void doDiff(string oldFile,string newFile)
  {
   oldInfo= new fileInfo(oldFile);
   newInfo = new fileInfo(newFile);
   //We don't process until we know both files really do exist.
   try
   {
    inputScan(oldInfo);
    inputScan(newInfo);
   }
   catch{
    Console.WriteLine("Read error.");
    return;
   }

   //Now that we've read all the lines, allocate some arrrays. ----->update
   blocklen= new int[(oldInfo.xlsSheet.UsedRange.Cells.Rows.Count*oldInfo.xlsSheet.UsedRange.Cells.Columns.Count >
       newInfo.xlsSheet.UsedRange.Cells.Rows.Count*newInfo.xlsSheet.UsedRange.Cells.Columns.Count ?
       oldInfo.xlsSheet.UsedRange.Cells.Rows.Count*oldInfo.xlsSheet.UsedRange.Cells.Columns.Count :
       newInfo.xlsSheet.UsedRange.Cells.Rows.Count*newInfo.xlsSheet.UsedRange.Cells.Columns.Count)+2];
   oldInfo.alloc();
   newInfo.alloc();

   //Do the work and print the results.
   transform();
   printOut();

   //Close Excel Application.  --->add
   oldInfo.xlsClose();
   newInfo.xlsClose();
  }

  /// <summary>
  /// inputScan Read the file specified by pinfo.file.
  ///    Places the lines of that file in the symbol table.
  ///    Sets pinfo.maxLine to the number of lines found.
  /// </summary>
  /// <param name="pinfo"></param>
  void inputScan(fileInfo pinfo){
   string lineBuffer;
   pinfo.maxLine =0;
   //--->update
   int j,i; // Excel file Cells (Row ,Column)
   for(j=1;j<pinfo.xlsSheet.UsedRange.Cells.Rows.Count;j++)
   {
    for(i=1;i<pinfo.xlsSheet.UsedRange.Columns.Rows.Count;i++)
    {
     lineBuffer=pinfo.xlsSheet.get_Range(pinfo.xlsSheet.Cells[j,i],pinfo.xlsSheet.Cells[j,i]).Text.ToString();
     if(lineBuffer!=string.Empty )
      storeline(lineBuffer,pinfo);
     else continue;
    }
   }
  }

  /// <summary>
  /// storeline Places line into symbol table.
  ///    Expects pinfo.maxLine initted: increments.
  ///    Places symbol table handle in pinfo.symbol.
  ///    Expects pinfo is either oldinfo or newinfo.
  /// </summary>
  /// <param name="lineBuffer"></param>
  /// <param name="pinfo"></param>
  void storeline(string lineBuffer,fileInfo pinfo)
  {
   int lineNum =++pinfo.maxLine ; //note, no line zero.
   if(lineNum>fileInfo.MAXLINECOUNT)
   {
    Console.WriteLine ("Exceeded,Stop.");
    return;
   }
   pinfo.symbol[lineNum]=node.addSymbol(lineBuffer,pinfo.Equals(oldInfo),lineNum);
  }

  /// <summary>
  /// transform Analyzes the file differences and leaves its finding in
  ///    the global arrays oldInfo.other, newInfo.other and blocklen.
  /// </summary>
  void transform()
  {
   int oldLine,newLine;
   int oldMax=oldInfo.maxLine+2;
   int newMax= newInfo.maxLine +2;

   for (oldLine=0;oldLine<oldMax;oldLine++)
    oldInfo.other[oldLine]=-1;
   for (newLine=0;newLine<newMax;newLine++)
    newInfo.other[newLine]=-1;

   scanUnique(); // Scan for lines used once in both files.
   scanAfter(); // Scan past sure-matches for non-unique blocks.
   scanBefore(); // Scan backwards form sure-matches
   scanBlocks(); // Fine the fronts and lengths of blocks.
  }

  /// <summary>
  /// scanUnique Scans for lines which are used exactly once in each file.
  /// </summary>
  void scanUnique()
  {
   int oldLine,newLine;
   node psymbol;

   for(newLine=1;newLine<=newInfo.maxLine ;newLine++)
   {
    psymbol=newInfo.symbol[newLine];
    if(psymbol.symbollsUnique()) // 1 use in each file
    {
     oldLine=psymbol.lineNum;
     newInfo.other[newLine]=oldLine;
     oldInfo.other[oldLine]=newLine;
    }
   }
   newInfo.other[0]=0;
   oldInfo.other[0]=0;
   newInfo.other[newInfo.maxLine +1]=oldInfo.maxLine +1;
   oldInfo.other[oldInfo.maxLine +1]=newInfo.maxLine +1;
  }

  /// <summary>
  /// scanAfter Expects both files in symtab, and oldInfo and newInfo valild.
  /// </summary>
  void scanAfter()
  {
   int oldLine,newLine;
   for(newLine=0;newLine<=newInfo.maxLine ;newLine++)
   {
    oldLine=newInfo.other[newLine];
    if(oldLine>=0) // is unique in old & new.
    {
     for(;;)  // Scan after there in both files.
     {
      if(++oldLine>oldInfo.maxLine ) break;
      if(oldInfo.other[oldLine]>=0) break;
      if(++newLine>newInfo.maxLine ) break;
      if(newInfo.other[newLine]>=0) break;
      if(newInfo.symbol[newLine]!=oldInfo.symbol[oldLine])
       break; // not same
      newInfo.other[newLine]=oldLine; // record a match
      oldInfo.other[oldLine]=newLine;
     }
    }
   }
  }
  
  /// <summary>
  /// scanBefore As scanAfter, except scans towards file fronts.
  ///    Assumes the off-end lines have benn marked as a match.
  /// </summary>
  void scanBefore()
  {
   int oldLine,newLine;
   for(newLine=newInfo.maxLine +1;newLine>0;newLine--)
   {
    oldLine=newInfo.other[newLine];
    if(oldLine>=0) //unique in each
    {
     for(;;)
     {
      if(--oldLine<=0) 
       break;
      if(oldInfo.other[oldLine]>=0) 
       break;
      if(--newLine<=0)
       break;
      if(newInfo.other[newLine]>=0)
       break;
      if(newInfo.symbol[newLine]!=oldInfo.symbol[oldLine])
       break;
      newInfo.other[newLine]=oldLine;
      oldInfo.other[oldLine]=newLine;
     }
    }
   }
  }

  /// <summary>
  /// scanBlocks Finds the beginnings and lenghts of blocks of matches.
  ///    Sets the blocklen array
  ///    Expects oldInfo valid.
  /// </summary>
  void scanBlocks()
  {
   int oldLine,newLine;
   int oldFront=0;  // line# of front of a block in old or 0
   int newLast=-1;  // newline's value during prev. iteration.
   for(oldLine=1;oldLine<=oldInfo.maxLine ;oldLine++)
    blocklen[oldLine]=0;
   blocklen[oldInfo.maxLine +1]=unReal; // starts a mthical blk
   for(oldLine=1;oldLine<=oldInfo.maxLine ;oldLine++)
   {
    newLine=oldInfo.other[oldLine];
    if(newLine<0) // no match: not in block
     oldFront=0;
    else   // match.
    {
     if(oldFront==0)oldFront=oldLine;
     if(newLine!=(newLast+1))oldFront=oldLine;
     ++blocklen[oldFront];
    }
    newLast=newLine;
   }
  }

  public static int idle=0,delete=1,insert=2,movenew=3,moveold=4,same=5,change=6;
  int printStatus;
  bool anyPrinted;
  int printOldLine,printNewLine; // line numbers in old & new file.
  
  /// <summary>
  ///  printOut Prints summary to stdout.
  ///    Expects all data structures have been filled out.
  /// </summary>
  void printOut()
  {
   printStatus=idle;
   anyPrinted=false;
   for(printOldLine=printNewLine=1;;)
   {
    if(printOldLine>oldInfo.maxLine )
    {
     newConsume();
     break;
    }
    if(printNewLine>newInfo.maxLine )
    {
     oldConsume();
     break;
    }
    if(newInfo.other[printNewLine]<0)
    {
     if(oldInfo.other[printOldLine]<0)
      showChange();
     else
      showInsert();
    }
    else if(oldInfo.other[printOldLine]<0)
     showDelete();
    else if(blocklen[printOldLine]<0)
     skipOld();
    else if(oldInfo.other[printOldLine].Equals(printNewLine))
     showSame();
    else
     showMove();
   }
   if(anyPrinted==true)
    Console.WriteLine (">>>>End of differences.");
   else
    Console.WriteLine (">>>>Files are Identical.");  
  }

  /// <summary>
  /// newConsume Part of printout. Have run out of old file.
  ///    Print the rest of the new file, as insert and/or moves.
  /// </summary>
  void newConsume()
  {
   for(;;)
   {
    if(printNewLine>newInfo.maxLine )
     break;  // end of file
    if(newInfo.other[printNewLine]<0)
     showInsert();
    else
     showMove();
   }
  }

  /// <summary>
  /// oldConsume Part of printout. Have run of new file.
  /// </summary>
  void oldConsume()
  {
   for(;;)
   {
    if(printOldLine>oldInfo.maxLine )
     break;  // end of file
    printNewLine=oldInfo.other[printOldLine];
    if(printNewLine<0)
     showDelete();
    else if(blocklen[printOldLine]<0)
     skipOld();
    else
     showMove();
   }
  }

  /// <summary>
  /// showDelete  Part of printout.
  /// </summary>
  void showDelete()
  {
   if(printStatus!=delete)
    Console.WriteLine(">>>>  Delete At  "+printOldLine);
   printStatus=delete;
   oldInfo.symbol[printOldLine].showSymbol();
   anyPrinted=true;
   printOldLine++;
  }
 
  /// <summary>
  /// showInsert  Part of printout.
  /// </summary>
  void showInsert()
  {
   if(printStatus==change)
    Console.WriteLine(">>>> Change To  ");
   else if (printStatus!=insert)
    Console.WriteLine(">>>> Insert Before  "+printOldLine);
   printStatus=insert;
   newInfo.symbol[printNewLine].showSymbol();
   anyPrinted=true;
   printNewLine++;
  }

  /// <summary>
  /// showChange  Part of printout.
  /// </summary>
  void showChange()
  {
   if(printStatus!=change)
    Console.WriteLine(">>>> "+printOldLine+" Change From");
   printStatus=change;
   oldInfo.symbol[printOldLine].showSymbol();
   anyPrinted=true;
   printNewLine++;
  }

  /// <summary>
  /// skipOld   Part of printout.
  /// </summary>
  void skipOld()
  {
   printStatus=idle;
   for(;;)
   {
    if(++printOldLine>oldInfo.maxLine )
     break;  // end of file.
    if(oldInfo.other[printOldLine]<0)
     break;  // end of block.
    if(blocklen[printOldLine]!=0)
     break;  // start of another.
   }
  }

  /// <summary>
  /// skipNew   Part of printout.
  /// </summary>
  void skipNew()
  {
   int oldLine;
   printStatus=idle;
   for(;;)
   {
    if(++printNewLine>newInfo.maxLine )
     break;  // end of file.     
    oldLine=newInfo.other[printNewLine];
    if(oldLine<0)
     break;  // end of block.
    if(blocklen[oldLine]!=0)
     break;  // start of another.
   }
  }

  /// <summary>
  /// showSame  Part of printout.
  /// </summary>
  void showSame()
  {
   int count;
   printStatus = idle;
   if(newInfo.other[printNewLine]!=printOldLine)
   {
    Console.WriteLine ("Bug In Line Referencing.");

   }
   count=blocklen[printOldLine];
   printOldLine+=count;
   printNewLine+=count;
  }


  /// <summary>
  /// showMove  Part of printout.
  /// </summary>
  void showMove()
  {
   int oldBlock = blocklen[printOldLine];
   int newother = newInfo.other[printNewLine];
   int newBlock = blocklen[newother];

   if(newBlock<0)
    skipNew();     // already printed.
   else if(oldBlock>=newBlock)  // assume new's blk moved.
   {
    blocklen[newother]=-1;  // stamp block as "printed".
    Console.WriteLine (">>>>  "+newother+" thru "
       +(newother+newBlock-1)+" Moved to Before "+ printOldLine);
    for(;newBlock>0;newBlock--,printNewLine++)
     newInfo.symbol[printNewLine].showSymbol();
    anyPrinted=true;
    printStatus=idle;
   }
   else       // assume old's block moved.
    skipOld();     // target line# not know, display later.
  }
 }

 /// <summary>
 /// Class "Node". The symbol table routines in this class all understand
 ///     the symbol table format, which is a binary tree.
 ///     The methods are: addSymbol, symbollsUnique, showSymbol.
 /// </summary>
 class node{     //The tree is made up of these nodes.
  node pleft,pright;
  internal int lineNum;

  static int freshNode=0,oldonce=1,newonce=2,bothnoce=3,other=4;

  int lineState;
  string line;

  static node panchor = null;

  /// <summary>
  /// Construct a new symbol table node and fill in its fields.
  /// </summary>
  /// <param name="pline">String A line of the text file</param>
  node(string pline){
   pleft=null;
   pright=null;
   lineState=freshNode;

   //linenum field is not always valid
   line=pline;
  }
  
  /// <summary>
  /// matchsymbol  Searches tree for a mathc to the line
  /// </summary>
  /// <param name="pline">String pline, a line of text</param>
  /// <returns></returns>
  static node matchSymbol(string pline){
   int comparison;
   node pnode = panchor;
   if(panchor== null)
    return panchor = new node(pline);
   for(;;){
    comparison = pnode.line.CompareTo(pline);
    if(comparison==0) // found
     return pnode;
    if(comparison<0){
     if(pnode.pleft==null){
      pnode.pleft=new node(pline);
      return pnode.pleft ;
     } 
     pnode= pnode.pleft ;
                }
    if(comparison>0)
    {
     if(pnode.pright ==null)
     {
      pnode.pright=new node(pline);
      return pnode.pright ;
     } 
     pnode= pnode.pright ;
    }   
   }
  }

  /// <summary>
  /// addSymbol(string pline) - Saves lines into the symbol table.
  /// </summary>
  /// <param name="pline"></param>
  /// <param name="inoldFile"></param>
  /// <param name="lineNum"></param>
  /// <returns>Returns a handle to the symtab entry for that unique line.</returns>
  internal static node addSymbol(string pline, bool inoldFile, int lineNum){
   node pnode;
   pnode = matchSymbol(pline);  // find the node in the tree.
   if(pnode.lineState.Equals(freshNode))
   {
    pnode.lineState=inoldFile?oldonce:newonce;
   }
   else if((pnode.lineState.Equals(oldonce)&&!inoldFile)||
    (pnode.lineState.Equals(newonce)&& inoldFile)){
    pnode.lineState =bothnoce;   
   }
   else
    pnode.lineState = other;
   if(inoldFile)
    pnode.lineNum=lineNum;
   return pnode;
  }

  /// <summary>
  /// symbollsUnique Arg is a ptr previously returned by addSymbol.
  /// </summary>
  /// <returns>
  /// Returns true if the line was add to the symbol table exactly once
  /// wiht inoldfile true, and exactly once with inoldfile false.
  /// </returns>
  internal bool symbollsUnique(){
   return(lineState==bothnoce);
  }

  /// <summary>
  /// showSymbol  Prints the line to stdout.
  /// </summary>
  internal void showSymbol(){
   Console.WriteLine(line);
  }
 }
}