/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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 3 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, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  plRTProjDirLight.cpp - Functions for the Projected Directional MAX light //
//  Cyan, Inc.                                                               //
//                                                                           //
//// Version History //////////////////////////////////////////////////////////
//                                                                           //
//  8.2.2001 mcn - Created.                                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "HeadSpin.h"
#include "plRTProjDirLight.h"
#include "plRTProjDirLightClassDesc.h"
#include "iparamm2.h"
#include "MaxPlasmaMtls/Layers/plLayerTex.h"
#include "MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h"
#include "MaxComponent/plMaxAnimUtils.h"
#include "plRTObjLightDesc.h"
#include "plRTLightBaseAnimDlgProc.h"


//// Static ClassDesc2 Get Functions //////////////////////////////////////////

plRTProjDirLightDesc    plRTProjDirLightDesc::fStaticDesc;


//// Dialog Proc //////////////////////////////////////////////////////////////

class plProjDirDlgProc : public plBaseLightProc
{
    public:
        BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
        {
            IParamBlock2        *pb = map->GetParamBlock();
            plRTProjDirLight    *lite = (plRTProjDirLight *)pb->GetOwner();
            PBBitmap            *bitmap;
            plLayerTex          *layer = (plLayerTex *)lite->GetProjMap();
            ICustButton         *bmSelectBtn;


            switch( msg )
            {
                case WM_COMMAND:
                    if( LOWORD( wParam ) == IDC_PROJ_MAPNAME )
                    {
                        BOOL selectedNewBitmap = layer->HandleBitmapSelection();
                        if( selectedNewBitmap )
                        {
                            bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_PROJ_MAPNAME ) );
                            bitmap = layer->GetPBBitmap();
                            bmSelectBtn->SetText( bitmap != nil ? (TCHAR *)bitmap->bi.Filename() : "");
                            ReleaseICustButton( bmSelectBtn );
                        }
                        return false;
                    }
                    break;

                case WM_INITDIALOG:

                    // Set projection map bitmap name
                    bmSelectBtn = GetICustButton( GetDlgItem( hWnd, IDC_PROJ_MAPNAME ) );
                    if( bmSelectBtn != nil )
                    {
                        bitmap = ( layer == nil ) ? nil : layer->GetPBBitmap();
                        if( bitmap != nil )
                            bmSelectBtn->SetText( (TCHAR *)bitmap->bi.Filename() );
                        else
                            bmSelectBtn->SetText( _T( "<none>" ) );
                        ReleaseICustButton( bmSelectBtn );
                    }

                    break;
            }

            return plBaseLightProc::DlgProc( t, map, hWnd, msg, wParam, lParam );
        }
        void DeleteThis() {};
};
static plProjDirDlgProc     gPPDirLiteDlgProc;

/// Include for the ParamBlock2 definition

#include "plRTProjDirLightPBDec.h"


///////////////////////////////////////////////////////////////////////////////
//// plRTProjPBAccessor Class Functions ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

plRTProjPBAccessor  plRTProjPBAccessor::fAccessor;

void    plRTProjPBAccessor::Set( PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t )
{
    plRTProjDirLight *obj = (plRTProjDirLight *)owner;
    IParamBlock2 *pb = obj->GetParamBlockByID( plRTProjDirLight::kBlkProj );
    
    switch( id )
    {
        case plRTProjDirLight::kProjMap:
/*          {
                if( pb->GetMap() )
                    pb->GetMap()->Invalidate( plRTProjDirLight::kProjMap );
                PBBitmap *thisMap = val.bm;
                obj->SetProjMap( &thisMap->bi );
            }
*/          break;
        default:
            break;
    }
}

void    plRTProjPBAccessor::Get( PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid )
{
}

///////////////////////////////////////////////////////////////////////////////
//// Projected Directional Light //////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

plRTProjDirLight::plRTProjDirLight()
{
    fIP = nil; 
    fLightPB = nil; 
    fProjPB = nil;
    fClassDesc = plRTProjDirLightDesc::GetDesc();
    fClassDesc->MakeAutoParamBlocks( this );

    fLightPB->SetValue(kLightColor, 0,  Color(255,255,255));
    SetHSVColor(0, Point3(255, 255, 255));
    
    fTex = nil;
    meshBuilt = 0; 
    
    IBuildMeshes(true);
}

ObjLightDesc *plRTProjDirLight::CreateLightDesc( INode *n, BOOL forceShadowBuf )
{
    return TRACKED_NEW DirLight( n, forceShadowBuf );
}

RefTargetHandle plRTProjDirLight::Clone( RemapDir &remap )
{
    plRTProjDirLight *obj = TRACKED_NEW plRTProjDirLight;

    obj->ReplaceReference( kRefDirLight, fLightPB->Clone( remap ) );
    BaseClone( this, obj, remap );

    return obj;
}

//// SubAnim /////////////////////////////////////////////////////////////////

Animatable  *plRTProjDirLight::SubAnim( int i )
{
    switch( i )
    {           
        case 0:
            return (Animatable *)fLightPB;
        case 1:
            return (Animatable *)fProjPB;
        default: 
            return nil;
    }
}

//// SubAnimName /////////////////////////////////////////////////////////////

TSTR    plRTProjDirLight::SubAnimName( int i ) 
{ 
    switch( i ) 
    {   
        case 0:
            return fLightPB->GetLocalName();
        case 1:
            return fProjPB->GetLocalName();
        default:
            return TSTR( "" );
    }
}

//// GetReference ////////////////////////////////////////////////////////////

RefTargetHandle plRTProjDirLight::GetReference( int i )
{
    switch( i )
    {
        case kRefMainRollout:
            return (RefTargetHandle)fLightPB;
        case kRefProjRollout:
            return (RefTargetHandle)fProjPB;
        default:
            return plRTLightBase::GetReference( i );
    }
}       

//// SetReference ////////////////////////////////////////////////////////////

void    plRTProjDirLight::SetReference( int ref, RefTargetHandle rtarg )
{
    switch( ref )
    {       
        case kRefMainRollout:
            fLightPB = (IParamBlock2 *)rtarg; 
            break;
        case kRefProjRollout:
            fProjPB = (IParamBlock2 *)rtarg;
            break;

        default:
            plRTLightBase::SetReference( ref, rtarg );
            break;
    }   
}

//// ParamBlock Functions ////////////////////////////////////////////////////

int plRTProjDirLight::NumParamBlocks()
{
    return plRTLightBase::NumParamBlocks() + 1;
}

IParamBlock2    *plRTProjDirLight::GetParamBlock( int i )
{
    switch ( i )
    {
        case 2: return fProjPB;
        default: return plRTLightBase::GetParamBlock( i );
    }
}

IParamBlock2    *plRTProjDirLight::GetParamBlockByID( BlockID id )
{
    if( fProjPB->ID() == id )
        return fProjPB;
    else
        return plRTLightBase::GetParamBlockByID( id );
}

//// GetProjMap //////////////////////////////////////////////////////////////

Texmap  *plRTProjDirLight::GetProjMap() 
{ 
    // If we don't have one, create one
    plLayerTex  *layer = (plLayerTex *)fProjPB->GetTexmap( kTexmap, 0 );
    if( layer == nil || layer->ClassID() != LAYER_TEX_CLASS_ID )
    {
        layer = TRACKED_NEW plLayerTex;
        fProjPB->SetValue( kTexmap, 0, (Texmap *)layer );

        IParamBlock2 *bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap );
        bitmapPB->SetValue( kBmpUseBitmap, 0, 1 );
    }

    // Backwards compatability here
    PBBitmap    *bitmap = fProjPB->GetBitmap( kProjMap, 0 );
    if( bitmap != nil )
    {
        layer->SetBitmap( &bitmap->bi );
        fProjPB->SetValue( kProjMap, 0, (PBBitmap *)nil );
    }

    if( layer )
    {
        const char* dbgTexName = layer->GetName();

        IParamBlock2 *bitmapPB = layer->GetParamBlockByID(plLayerTex::kBlkBitmap);
        hsAssert(bitmapPB, "LayerTex with no param block");

        int noCompress = fProjPB->GetInt(kProjNoCompress);
        int noMip = fProjPB->GetInt(kProjNoMip);
        bitmapPB->SetValue(kBmpNonCompressed, TimeValue(0), noCompress);
        bitmapPB->SetValue(kBmpNoFilter, TimeValue(0), noMip);
    }
    return (Texmap *)layer;
}
    
//// IBuildMeshes ////////////////////////////////////////////////////////////

void    plRTProjDirLight::IBuildMeshes( BOOL isnew ) 
{
    BuildStaticMeshes();

    fMesh = staticMesh[ plRTLightBase::RT_OMNI + 1 ];
}

//// GetLocalBoundBox ////////////////////////////////////////////////////////

void    plRTProjDirLight::GetLocalBoundBox( TimeValue t, INode *node, ViewExp *vpt, Box3 &box )
{
    Point3  loc = node->GetObjectTM( t ).GetTrans();
    float   scaleFactor = vpt->NonScalingObjectSize() * vpt->GetVPWorldWidth( loc ) / 360.0f;
    float   width, height, depth;

    box = fMesh.getBoundingBox();
    // Because we want to scale about the origin, not the box center, we have to do this funky offset
    Point3  boxCenter = box.Center();
    box.Translate( -boxCenter );
    box.Scale( scaleFactor );
    boxCenter *= scaleFactor;
    box.Translate( boxCenter );

    if( ( extDispFlags & EXT_DISP_ONLY_SELECTED ) )
    {
        fProjPB->GetValue( kWidth, t, width, FOREVER );
        fProjPB->GetValue( kHeight, t, height, FOREVER );
        fProjPB->GetValue( kRange, t, depth, FOREVER );
        width /= 2.f;
        height /= 2.f;

        box += Point3( -width, -height, 0.f );
        box += Point3( width, height, -depth );
    }
}

//// DrawConeAndLine /////////////////////////////////////////////////////////

int     plRTProjDirLight::DrawConeAndLine( TimeValue t, INode* inode, GraphicsWindow *gw, int drawing ) 
{
    Matrix3 tm = inode->GetObjectTM( t );
    gw->setTransform( tm );
    gw->clearHitCode();

    if( extDispFlags & EXT_DISP_ONLY_SELECTED )
        DrawCone( t, gw, 500 );

    return gw->checkHitCode();
}

//// DrawCone ////////////////////////////////////////////////////////////////
//  Function called by MAX to render the cone shape in the viewport for this
//  light. Note that this is the cone, not the actual object itself!

void    plRTProjDirLight::DrawCone( TimeValue t, GraphicsWindow *gw, float dist ) 
{
    Point3  nearPts[ 5 ], farPts[ 5 ], u[ 3 ];
    int     i;
    float   width, height;


    // Get values
    fProjPB->GetValue( kWidth, t, width, FOREVER );
    fProjPB->GetValue( kHeight, t, height, FOREVER );
    fProjPB->GetValue( kRange, t, dist, FOREVER );

    // Draw a rectangle showing the extents of the light (width, height and depth)
    IBuildRectangle( width, height, 0, nearPts );
    IBuildRectangle( width, height, -dist, farPts );

    gw->setColor( LINE_COLOR, GetUIColor( COLOR_HOTSPOT ) );

    gw->polyline( 4, nearPts, nil, nil, true, nil );
    gw->polyline( 4, farPts, nil, nil, true, nil );
    for( i = 0; i < 4; i++ )
    {
        u[ 0 ] = nearPts[ i ];
        u[ 1 ] = farPts[ i ];
        gw->polyline( 2, u, nil, nil, false, nil );
    }
}

//// IBuildRectangle /////////////////////////////////////////////////////////

void    plRTProjDirLight::IBuildRectangle( float width, float height, float z, Point3 *pts )
{
    width /= 2.f;
    height /= 2.f;

    pts[ 0 ] = Point3( -width, -height, z );
    pts[ 1 ] = Point3( width, -height, z );
    pts[ 2 ] = Point3( width, height, z );
    pts[ 3 ] = Point3( -width, height, z );
}

//// GetFallsize /////////////////////////////////////////////////////////////
//  To get using-light-as-camera-viewport to work

float   plRTProjDirLight::GetFallsize( TimeValue t, Interval &valid )
{
    if( fProjPB == nil )
        return -1.f;

    return fProjPB->GetFloat( kWidth, t );
}

//// GetAspect ///////////////////////////////////////////////////////////////
//  To get using-light-as-camera-viewport to work

float   plRTProjDirLight::GetAspect( TimeValue t, Interval &valid )
{
    if( fProjPB == nil )
        return -1.f;

    return( fProjPB->GetFloat( kHeight, t ) / fProjPB->GetFloat( kWidth, t ) );
}

//// GetTDist ////////////////////////////////////////////////////////////////
//  To get using-light-as-camera-viewport to work

float   plRTProjDirLight::GetTDist( TimeValue t, Interval &valid )
{
    return 0.f;
    if( fProjPB == nil )
        return -1.f;

    return fProjPB->GetFloat( kRange, t );
}

//// SetFallsize /////////////////////////////////////////////////////////////
//  To get using-light-as-camera-viewport to work

void    plRTProjDirLight::SetFallsize( TimeValue time, float f )
{
    if( fProjPB != nil )
        fProjPB->SetValue( kWidth, time, f );
}

//// EvalLightState //////////////////////////////////////////////////////////
//  To get using-light-as-camera-viewport to work

RefResult plRTProjDirLight::EvalLightState( TimeValue t, Interval& valid, LightState *ls )
{
    ls->type = DIRECT_LGT;
    if( fLightPB->GetInt( kLightOn, t ) == true )
        ls->color = GetRGBColor( t, valid );
    else
        ls->color = Color( 0, 0, 0 );
    ls->on = fLightPB->GetInt( kLightOn, t );
    ls->intens = GetIntensity( t, valid );
    
    float   fall = fProjPB->GetFloat( kWidth, t );
    if( fall < fProjPB->GetFloat( kHeight, t ) )
        fall = fProjPB->GetFloat( kHeight, t );

    ls->hotsize = ls->fallsize = fall / 2.f;
    ls->useNearAtten = false;
    ls->useAtten = false;
    ls->shape = RECT_LIGHT;

    ls->aspect = fProjPB->GetFloat( kHeight, t ) / fProjPB->GetFloat( kWidth, t );
    ls->overshoot = false;
    ls->shadow = false;
    ls->ambientOnly = false;
    ls->affectDiffuse = fLightPB->GetInt( kAffectDiffuse, t );
    ls->affectSpecular = fLightPB->GetInt( kSpec, t );

    return REF_SUCCEED;
}