// Description:
//   Block Model
//
// Copyright (C) 2003 Frank Becker
//
// 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
//
#ifndef __BlockModel_hpp__
#define __BlockModel_hpp__

#include <list>
#include <fstream>

#include <Point.hpp>
#include <R250.hpp>

enum Direction
{
    eLeft,	// -x
    eRight,	// +x

    eDown,	// -y
    eUp,	// +y

    eIn,	// -z
    eOut	// +z
};

typedef std::list<Point3Di*> ElementList;

class BlockViewBase;
class ziStream;

class BlockModel
{
public:
    BlockModel( int w, int h, int d, int level, R250 &r);
    ~BlockModel();

    bool init( void);

    void addBlock( void);
    bool loadBlocks( void);
    bool readBlock( ziStream &infile, int numElems, int &linecount);

    int getWidth( void){ return _width;}
    int getHeight( void){ return _height;}
    int getDepth( void){ return _depth;}
    int getLevel( void){ return _level;}
    unsigned int getScore( void){ return _score;}
    unsigned int getElementCount( void){ return _elementCount;}

    ElementList &getElementListNorm( void){ return *_elementListNorm;}
    ElementList &getElementList( void){ return *_elementList;}
    ElementList &getLockedElementList( void){ return *_lockedElementList;}
    Point3Di & getBlockOffset( void){ return _offset; }
    Point3Di & getOrientation( void){ return _orientation; }

    void moveBlock( Direction dir);
    void rotateBlock( const Point3Di &r1, 
		      const Point3Di &r2, 
		      const Point3Di &r3, 
		      const Point3Di &axis);

    void registerView( BlockViewBase *view)
    {
	_view = view;
    }

    int numBlocksInPlane( unsigned int plane)
    {
	if( plane>=(unsigned int)_depth) return 0;
	return _planesLockCount[plane];
    }

    bool update( void);

protected:
    bool verifyAndAdjust( void);
    bool canDrop( void);
    void checkPlanes(void);

    void updateNextDrop( float delta);
    void updateDropDelay( void);

    bool lockElement( Point3Di &a);
    bool elementLocked( Point3Di &a);

private:
    int _width;
    int _height;
    int _depth;
    int _level;
    R250 &_r250;

    float _nextDrop;
    float _dropDelay;

    unsigned char *_planesmem;
    unsigned char **_planes;
    unsigned char *_planesLockCount;

    unsigned int _numBlocks;
    ElementList *_blockList[100];

    ElementList *_elementListNorm;
    ElementList *_elementList;
    ElementList *_tmpElementList;
    ElementList *_lockedElementList;

    Point3Di _orientation;
    Point3Di _offset;
    bool _freefall;

    unsigned int _score;
    int _elementCount;

    BlockViewBase *_view;
};

#endif
