/*
 *   ksame 0.4 - simple Game
 *   Copyright (C) 1997,1998  Marcus Kreutzberger
 * 
 *   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; either version 2 of the License, or
 *   (at your option) any later version.
 *    
 *   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; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "StoneField.h"

StoneFieldState::StoneFieldState(StoneField &stonefield):stonefield(stonefield) {
     field=new unsigned char[stonefield.maxstone];
     for (int i=0;i<stonefield.maxstone;i++)
	  field[i]=stonefield.field[i].color;

     colors=stonefield.colors;
     board=stonefield.board;
     score=stonefield.score;
     gameover=stonefield.gameover;
}

StoneFieldState::~StoneFieldState() {
     delete[] field;
}

void
StoneFieldState::restore() {
     for (int i=0;i<stonefield.maxstone;i++) {
	  stonefield.field[i].color=field[i];
	  stonefield.field[i].changed=true;
	  stonefield.field[i].marked=false;
     }
     
     stonefield.colors=colors;
     stonefield.board=board;
     stonefield.score=score;
     stonefield.marked=0;
     stonefield.gameover=gameover;
}



StoneField::StoneField(int width, int height,
		       int colors,unsigned int board,
		       bool undoenabled) {
     ASSERT(width>0);
     ASSERT(height>0);
     
     if (undoenabled) undolist=new QList<StoneFieldState>;
     else undolist=0;
     
     sizex=width;
     sizey=height;
     maxstone=sizex*sizey;
     field=new Stone[maxstone];
     newGame(board,colors);
}

StoneField::~StoneField() {  
     delete[] field;
     if (undolist) delete undolist;
     debug("~StoneField\n");
}

int
StoneField::width() {
     return sizex;
}

int
StoneField::height() {
     return sizey;
}

void 
StoneField::newGame(unsigned int board,int colors) {
     debug("StoneField::newgame board %i colors %i",board,colors);
     if (colors<1) colors=3;
     if (colors>7) colors=7;
     this->colors=colors;
     this->board=board;
     reset();
}

void 
StoneField::reset() {
     random.setSeed(board);
     
     Stone *stone=field;
     for (int i=0;i<maxstone;i++,stone++) {
	  stone->color=1+random.getLong(colors);
	  stone->marked=false;
	  stone->changed=true;
     }
    
     gameover=-1;
     score=0;
     marked=0;

     if (undolist) {
	  undolist->setAutoDelete(true);
	  undolist->clear();
     }


     int c[7];
     int j;
     for (j=0;j<7;j++) c[j]=0;
     
     for (j=0,stone=field;j<maxstone;j++,stone++) {
	  c[stone->color]++;
     }
     debug("red    %i",c[1]);
     debug("blue   %i",c[2]);
     debug("yellow %i",c[3]);
     debug("green  %i",c[4]);
        
}

int 
StoneField::map(int x,int y) {
     if (x<0||y<0||x>=sizex||y>=sizey)  
	  return -1;
     return x+y*sizex;
}

int
StoneField::mark(int x,int y,bool force) {
     int index=map(x,y);
     
     if (index<0) {
	  unmark();
	  return 0;
     }

     if (field[index].marked) return -1;
     unmark();
     
     mark(index,field[index].color);
     
     if (marked==1&&!force) {
	  field[index].marked=false;
	  marked=0;
     }
     return marked;
}

void 
StoneField::mark(int index,unsigned char color) {
     if ( index<0 || index>=maxstone ) return;

     Stone &stone=field[index];

     if (stone.marked) return;
     
     if (!stone.color || stone.color!=color) return;
    
     stone.changed=true;
     stone.marked=true;
     marked++;

     // mark left
     if ((index%sizex)!=0) mark(index-1,color);
     // mark right
     if (((index+1)%sizex)!=0) mark(index+1,color);     
     // mark upward
     if (index>=sizex) mark(index-sizex,color);
     // mark downward
     if (index<(sizex-1)*sizey) mark(index+sizex,color);
}

void 
StoneField::unmark() {
     if (!marked) return;
     
     Stone *stone=field;
     for (int i=0;i<maxstone;i++,stone++) {
	  stone->marked=false;
	  stone->changed=true;
     }
     marked=0;
}

int 
StoneField::remove(int x,int y,bool force) {
     int index=map(x,y);
	  
     if (index<0) return 0;
     
     if (!field[index].marked) {
	  mark(x,y,force);
     }
     
     if (!marked) return 0;
     
     // remove a single stone??
     if (marked==1&&!force) return 0;
     
     // add current field to undolist
     if (undolist)
	  undolist->append(new StoneFieldState(*this));

     // increase score
     if (marked>2)
	  score+=(marked-2)*(marked-2);
     
     // remove marked stones
     Stone *stone=field;
     for (int i=0;i<maxstone;i++,stone++) {
	  if (stone->marked) {
	       stone->color=0;
	       stone->changed=true;
	       stone->marked=false;
	  }
     }
     int removed=marked;     
     marked=0;
     
     // collapse field
     int col,j,i1,i2;
     
     for (col=0;col<sizex;col++) {
	  i1=col+maxstone-sizex;
	  while ( i1>=0 && field[i1].color ) i1-=sizex;
	  i2=i1;
	  while (i2>=0) {
	       while ( i2>=0 && !field[i2].color ) i2-=sizex;
	       while ( i2>=0 && field[i2].color ) {
		    field[i1].color=field[i2].color;
		    field[i1].changed=true;
		    field[i2].color=0;
		    field[i2].changed=true;
		    i1-=sizex;
		    i2-=sizex;
	       }
	  }
     }
     
     for (col=0;col<sizex;col++) {
	  i1=col+maxstone-sizex;
	  while ( i1< maxstone && field[i1].color ) i1++;
	  i2=i1;
	  while (i2<maxstone) {
	       while ( i2<maxstone && !field[i2].color) i2++;
	       while ( i2<maxstone && field[i2].color ) {
		    for (j=0;j<maxstone;j+=sizex) {
			 field[i1-j].color=field[i2-j].color;
			 field[i1-j].changed=true;
			 field[i2-j].color=0;
			 field[i2-j].changed=true;
		    }
		    i1++;
		    i2++;
	       }
	  }
     }
     
     // add a bonus, if field is empty
     if (!field[maxstone-sizex].color) score+=1000;
     
     // gameover is undefined
     gameover=-1;
     return removed;
}

int
StoneField::undo(int count) {
     if (!undolist||count<=0||undolist->isEmpty()) return 0;
     int undocount=1;
     StoneFieldState *state=0;
     undolist->setAutoDelete(true);
     while (--count>0) {
	  if (undolist->count()==1) break;
	  undolist->removeLast();
	  undocount++;
     }
     state=undolist->getLast();
     ASSERT(state);
     state->restore();
     undolist->removeLast();
     return undocount;
}

bool
StoneField::isGameover() {
     register int i=maxstone-1;;
     register unsigned char color;



     if (gameover>=0) return (bool)gameover;
     // debug("-->gameover");
     
     while (i>=0) {
	  // debug ("%i %i",i,field[i].color);
	  // ignore empty fields
	  while ( i>=0 && field[i].color==0 ) i--;
	  // Wenn Stein gefunden, 
	  // dann die Nachbarn auf gleiche Farbe pruefen.
	  while (  i>=0 && (color=field[i].color) ) {
	       // check left
	       if ( (i%sizex)!=0 && field[i-1].color==color) 
		    goto check_gameover;
	       // check upward
	       if ( i>=sizex && field[i-sizex].color==color) 
		    goto check_gameover;
	       i--;
	  }
     }
 check_gameover:
     gameover=(i<0);
     //  debug("<--gameover");
     return (bool)gameover;
}

int
StoneField::getBoard() {
     return board;
}

int
StoneField::getScore() {
     return score; 
}

int
StoneField::getColors() {
     return colors;
}

int
StoneField::getMarked() {
     return marked;
}

int
StoneField::getFieldSize() {
     return maxstone;
}

struct Stone *
StoneField::getField() {
     return field;
}







