/*
 * server/Sector.cc:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

#include "Sector.h"
#include "SectorSpecial.h"
#include "Zone.h"
#include "Lightmap.h"

#include "../util/Persist.h"
#include "../util/Array.h"


#include <iostream>

/**
 * Sector format:
 *  short num_sverts
 *  short floorHeight
 *  short ceilHeight
 *  unsigned char  brightness;
 *  num_sverts: < short v
 */

static int last_num = 0;

Sector::Sector( istream & is, 
                const class Array<float *> & vs,
                const class Array<const char *> & ts,
                const class Array<class Lightmap *> &lm,
                class Zone * zone )
        : _zone( 0 ),
          floorTex( 0 ),
          ceilingTex( 0 ),
          floor_lightmap( 0 ),
          ceiling_lightmap( 0 ),
          clientSector( 0 ),
          spec( 0 )
{

        int numVerts        = Persist::readShort( is );
        _surfaceHeights[0]  = Persist::readShort( is );
        _surfaceHeights[1]  = Persist::readShort( is );

        short num_vis          = Persist::readShort( is );
        short num_visFloors    = Persist::readShort( is );
        short num_visCeilings  = Persist::readShort( is );
        short num_visLines     = Persist::readShort( is );
        
        short floor_tex   = Persist::readShort( is );
        short ceiling_tex = Persist::readShort( is );
        short floor_lm    = Persist::readShort( is );
        short ceiling_lm  = Persist::readShort( is );
        short special_num = Persist::readShort( is );        // special

        if ( floor_lm >= 0 ) {
                floor_lightmap = lm[ floor_lm ];
        }
        if ( ceiling_lm >= 0 ) {
                ceiling_lightmap = lm[ ceiling_lm ];
        }

        if ( special_num >= 0 ) {
                spec = zone->getSectSpecials()[ special_num ];
                spec->sect = this;
        }
        else
                spec = 0;
        
        //
        // get the texture names
        //

        if ( floor_tex >= 0 )
                floorTex = ts[ floor_tex ];

        if ( ceiling_tex >= 0 )
                ceilingTex = ts[ ceiling_tex ];

        verts.setSize( numVerts );
        
        _myLines.setSize( numVerts );
        _myLines.cur_index = 0; // start the counter at the beginning for addElement() to work

        float max_verts[2] = { -999999, -999999 };
        float min_verts[2] = {  999999,  999999 };
        
        for ( int i = 0; i < numVerts; ++i ) {
                short ind = Persist::readShort( is );
                verts[ i ] = vs[ind];

                max_verts[0] = max( max_verts[0], verts[i][0] );
                max_verts[1] = max( max_verts[1], verts[i][1] );
                min_verts[0] = min( min_verts[0], verts[i][0] );
                min_verts[1] = min( min_verts[1], verts[i][1] );
        }
        
        _offset[0] = min_verts[0];
        _offset[1] = min_verts[1];

        _extent[0] = max_verts[0] - min_verts[0];
        _extent[1] = max_verts[1] - min_verts[1];
        
        //
        // calculate the centerpoint
        //

        _centerPoint[0] = ( min_verts[0] + max_verts[0] ) / 2;
        _centerPoint[1] = ( min_verts[1] + max_verts[1] ) / 2;
        _centerPoint[2] = ( _surfaceHeights[0] + _surfaceHeights[1] ) / 2;
        
        _visibleSectors.setSize( num_vis );
        for ( int i = 0; i < num_vis; ++i ) {
                _visibleSectors[i] = Persist::readShort( is );
        }

        _visibleFloors.setSize( num_visFloors );
        for ( int i = 0; i < num_visFloors; ++i ) {
                _visibleFloors[i] = Persist::readShort( is );
        }

        _visibleCeilings.setSize( num_visCeilings );
        for ( int i = 0; i < num_visCeilings; ++i ) {
                _visibleCeilings[i] = Persist::readShort( is );
        }

        _visibleLines.setSize( num_visLines );
        for ( int i = 0; i < num_visLines; ++i ) {
                _visibleLines[i] = Persist::readShort( is );
        }

        _zone = zone;
        _num = ++last_num;
}

void Sector::writeSector( class ostream & os ) const {
        Persist::writeShort( os, verts.length );

        if ( spec && spec->isLift() )
                Persist::writeShort( os, _surfaceHeights[0] - spec->cur_travel );
        else
                Persist::writeShort( os, _surfaceHeights[0] );
        
        if ( spec && spec->isDoor() )
                Persist::writeShort( os, _surfaceHeights[1] + spec->cur_travel );
        else
                Persist::writeShort( os, _surfaceHeights[1] );
        
        Persist::writeShort( os, (short) _visibleSectors.length );
        Persist::writeShort( os, (short) _visibleFloors.length );
        Persist::writeShort( os, (short) _visibleCeilings.length );
        Persist::writeShort( os, (short) _visibleLines.length );

        const class Array<const char *> & textures      = _zone->getTexnames();
        const class Array<class Lightmap *> & lightmaps = _zone->getLightmaps();

        short floor_tex   = (short) textures.findElement(  floorTex );
        short ceiling_tex = (short) textures.findElement(  ceilingTex );
        short floor_lm    = (short) lightmaps.findElement( floor_lightmap );
        short ceiling_lm  = (short) lightmaps.findElement( ceiling_lightmap );
        
        Persist::writeShort( os, floor_tex );
        Persist::writeShort( os, ceiling_tex );
        Persist::writeShort( os, floor_lm );
        Persist::writeShort( os, ceiling_lm );
        
        short spec_index = -1;

        if ( spec ) {
                const Array<SectorSpecial *> & specs = _zone->getSectSpecials();

                for ( int i = 0; i < specs.length; ++i ) {
                        if ( specs[i] == spec ) {
                                spec_index = i;
                                break;
                        }
                }
        }
         
        Persist::writeShort( os, spec_index ); // special

        const class Array<float *> & vertexes = _zone->getVertexes();
        
        for ( int i = 0; i < verts.length; ++i ) {
                
                short ind = vertexes.findElement( verts[i] );

                if ( ind == -1 ) {
                        cout << " error verts == -1 in Sector::writeSector" << endl;
                }

                Persist::writeShort( os, ind );
        }
        
        for ( int i = 0; i < _visibleSectors.length; ++i ) {
                Persist::writeShort( os, (short) _visibleSectors[i] );
        }
        for ( int i = 0; i < _visibleFloors.length; ++i ) {
                Persist::writeShort( os, (short) _visibleFloors[i] );
        }
        for ( int i = 0; i < _visibleCeilings.length; ++i ) {
                Persist::writeShort( os, (short) _visibleCeilings[i] );
        }
        for ( int i = 0; i < _visibleLines.length; ++i ) {
                Persist::writeShort( os, (short) _visibleLines[i] );
        }

}

Sector::~Sector() {

}

bool Sector::canSeeSector( const class Sector * osect ) const {

        if ( this == osect )
                return true;
        
        return ( _visibleSectors.findElement( osect->_num ) > -1 );
}

int Sector::getSurfaceVelocity( SurfaceType which ) const {
        if ( spec ) {

                if ( which == FLOOR && spec->isLift() )
                        return spec->isExtending() ? spec->speed : - spec->speed;
                if ( which == CEILING && spec->isDoor() )
                        return spec->isExtending() ? - spec->speed : spec->speed;
        }
        return 0;
}        
