/*
   vcb.cc

   Vigenere Cipher Breaker
    Copyright (C) 1997,1998 Robert Muth <muth@cs.arizona.edu>

    This program 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; version 2 of June 1991.

    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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program in form of the file COPYING; 
    if not, write to the 

    Free Software Foundation, Inc. <http://www.fsf.org>
    59 Temple Place, Suite 330, 
    Boston, MA 02111-1307  USA
*/



//=========================================================
//
// Vigenere Cipher Breaker
//
//=========================================================

#include <stdlib.h>
#include <unistd.h>

#include "text.hh"
#include "vign.hh"
#include "stat.hh"

#define tab "\t"


struct
{
    int DebugFlag;
    int LineLength;
    int Penalty;
}
Global=
{0,75,-1000};

//=========================================================
// usage
//=========================================================

int usage()
{
    cerr << "usage: vcb [flags] trigram ciphertext" << endl
         << "flags" << endl
         << tab << "-d" << tab << "Print Debug Info" 
         << tab << "-l linelength" << endl
         << "Set Line Length (default: 75)" << endl
         << tab << "-p penalty" << tab
         << "Sepcify trigram penalty (default: -1000)" << endl;
    
    return -1;
}

//=========================================================
// ImproveTrigrams
//=========================================================

void ImproveTrigrams(VIGN *v, TEXT *t, STAT *s)
{
    int i,j1,j2,j3,change;
    
    int best = s->EvalTextVign3(t,v);

    do
    {
        change = 0;
        for( i=0; i<v->Length()-1; i++ )
        {
            int save1=v->Get(i),
                save2=v->Get(i+1);
              //save3=v->Get(i+2)
            
            for( j1=0; j1<27; j1++ )
                for( j2=0; j2<27; j2++ )
                    //                    for( j3=0; j3<27; j3++ )
                {
                    v->Set(i+0,j1);
                    v->Set(i+1,j2);
                    //                    v->Set(i+2,j3);
                    
                    int cur = s->EvalTextVign3(t,v);
                    if( cur > best )
                    {
                        save1 = j1;
                        save2 = j2;
                        //                        save3 = j3;
                        best = cur;
                        change = 1;
                    }
                }
            v->Set(i+0,save1);
            v->Set(i+1,save2);
            //            v->Set(i+2,save3);
            
        }
        
    }while( change );
}


//=========================================================
// MAIN
//=========================================================

int main(int argc, char *argv[])
{
    int i;
    
    while( EOF != (i = getopt(argc,argv,"sdl:")) )
    {
        switch(i)
        {
        case 'd':
            Global.DebugFlag = 1;
            break;
        case 'l':
            Global.LineLength = atoi(optarg);
            break;
        case '?':
        case 'h':
        default:
            return usage();
            break;
        }
    }

    argc -= optind;
    argv += optind;

    if( argc != 2 )
        return usage();
    

    if( Global.DebugFlag )
        cerr << "Reading Trigrams from: " << argv[0] << endl;

    STAT s(argv[0],Global.Penalty);

    if( Global.DebugFlag )
        cerr << "Reading Text from: " << argv[1] << endl;

    TEXT t(argv[1]);


    VIGN best(1);
        
    for( i=7; i<13; i++ )
    {
        VIGN v(i);

        if( Global.DebugFlag )
        {
            cerr << "Current Key Length: " << v.Length() << endl;
        }
        
        ImproveTrigrams(&v,&t,&s);

        if( s.EvalTextVign3(&t,&v) > s.EvalTextVign3(&t,&best) )
        {
            best = v;
            
            if( Global.DebugFlag )
            {
                cerr << "Improved: " << s.EvalTextVign3(&t,&best) << endl;
                v.PrintVign(cerr);
                t.PrintVign(cerr,Global.LineLength,&v);
                cerr << endl;
            }
        }
    }

    cout << "BEST:" << endl;
    best.PrintVign(cout);
    cout << "Trigram   Score: " << s.EvalTextVign3(&t,&best) << endl;
    t.PrintVign(cout,75,&best);    
    
    return 0;
}

//=========================================================
// EOF
//=========================================================
