/*==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==*/
#include "hsTypes.h"
#include "plLayerTex.h"
#include "plLayerTexBitmapPB.h"
#include "../plDetailCurveCtrl.h"

#if 1
class BMTexPBAccessor;
extern BMTexPBAccessor bmtex_accessor;

class BMCropper : public CropCallback
{
    IParamBlock2 *fPBlock;

public:
    BMCropper(IParamBlock2* pblock) : fPBlock(pblock) {}

    float GetInitU() { return fPBlock->GetFloat(kBmpClipU); }
    float GetInitV() { return fPBlock->GetFloat(kBmpClipV); }
    float GetInitW() { return fPBlock->GetFloat(kBmpClipW); }
    float GetInitH() { return fPBlock->GetFloat(kBmpClipH); }
    BOOL GetInitMode() { return fPBlock->GetInt(kBmpCropPlace); }
    void SetValues(float u, float v, float w, float h, BOOL md);
    void OnClose();
};

class BitmapDlgProc : public ParamMap2UserDlgProc
{
    friend class BMTexPBAccessor;
    
    
    PBBitmap    *fLastBMap;
    bool        fSettingDetailValues;
    
public:
    /// Called to update the controls of the dialog
    /// Note: we're bad that we use a static here, but 
    virtual void    Update( TimeValue t, Interval &valid, IParamMap2 *map )
    {
        ICustButton     *bmSelectBtn;
        IParamBlock2    *pblock;
        int             width, height;
        
        
        ParamMap2UserDlgProc::Update( t, valid, map );
        
        if( fSettingDetailValues )
        {
            // We're getting an update just because we changed detail values, so we
            // know we don't have to do anything ourselves
            return;
        }
        
        pblock = map->GetParamBlock();
        
        // Update texture map button
        bmSelectBtn = GetICustButton( GetDlgItem( map->GetHWnd(), IDC_LAYER_NAME ) );
        PBBitmap *pbbm = pblock->GetBitmap( kBmpBitmap, t );
        if( pbbm )
        {
            if( pbbm != fLastBMap )
            {
                bmSelectBtn->SetText( (TCHAR *)pbbm->bi.Filename() );
                
                // Init values for clamping spinners to powers of 2
                width = IFloorPow2( pbbm->bi.Width() );
                map->SetRange( kBmpExportWidth, 4.f, (float)width );
                
                height = IFloorPow2( pbbm->bi.Height() );
                map->SetRange( kBmpExportHeight, 4.f, (float)height );
                
                IClampTexSizeSpinner( t, map, true );
                ISetDetailCurveNumLevels( map, t );
            }
        }
        else if( pbbm != fLastBMap )
            bmSelectBtn->SetText( _T( "None" ) );
        
        fLastBMap = pbbm;
        
        ReleaseICustButton( bmSelectBtn );
        
        // Update detail curve control
        HWND dlg = map->GetHWnd();
        
        plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( dlg, IDC_DETAIL_CURVE_CTRL );
        if( ctrl == NULL )
        {
            // The control hasn't been created, so create it already!
            HWND                basis;
            RECT                r;
            
            // Create the detail map control
            basis = GetDlgItem( dlg, IDC_DETAIL_SAMPLE );
            GetClientRect( basis, &r );
            MapWindowPoints( basis, dlg, (POINT *)&r, 2 );
            
            ctrl = TRACKED_NEW plDetailCurveCtrl( dlg, IDC_DETAIL_CURVE_CTRL, &r );
        }
        
        EnableWindow( GetDlgItem( dlg, IDC_DETAIL_CURVE_CTRL ), (BOOL)pblock->GetInt( kBmpUseDetail, t ) );
        
        if( ctrl != NULL )
        {
            ctrl->SetStartPoint( (float)pblock->GetInt( kBmpDetailStartSize, t ) / 100.f,
                (float)pblock->GetInt( kBmpDetailStartOpac, t ) / 100.f );
            ctrl->SetEndPoint(   (float)pblock->GetInt( kBmpDetailStopSize, t ) / 100.f,
                (float)pblock->GetInt( kBmpDetailStopOpac, t ) / 100.f );
        }
        
    }
    
    virtual BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        static ICustButton* bmSelectBtn;
        
        switch (msg)
        {
        case WM_INITDIALOG:
            fLastBMap = NULL;
            fSettingDetailValues = false;
            break;
            
            /// Note: the following *could* be done in the accessor, except that you end up in an
            /// infinite loop updating the values. Not good. 
        case CC_SPINNER_CHANGE: 
            
            if( LOWORD( wParam ) == IDC_EXPORTWIDTH_SPINNER )
                IClampTexSizeSpinner( t, map, true );
            
            else if( LOWORD( wParam ) == IDC_EXPORTHEIGHT_SPINNER )
                IClampTexSizeSpinner( t, map, false );
            
            break;
            
            // Message from the detail curve that a point got dragged
        case PL_DC_POINT_DRAGGED:
            {
                plDetailCurveCtrl   *ctrl = (plDetailCurveCtrl *)lParam;
                IParamBlock2        *pblock = map->GetParamBlock();
                float               x, y;
                
                
                fSettingDetailValues = true;
                
                if( wParam == PL_DC_START_POINT )
                {
                    ctrl->GetStartPoint( x, y );
                    pblock->SetValue( kBmpDetailStartSize, t, (int)( x * 100.f ) );
                    pblock->SetValue( kBmpDetailStartOpac, t, (int)( y * 100.f ) );
                }
                else
                {
                    ctrl->GetEndPoint( x, y );
                    pblock->SetValue( kBmpDetailStopSize, t, (int)( x * 100.f ) );
                    pblock->SetValue( kBmpDetailStopOpac, t, (int)( y * 100.f ) );
                }
                
                map->UpdateUI( t );
                fSettingDetailValues = false;
            }
            return 0;
            
        case WM_COMMAND:
            if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTWIDTH )
                IClampTexSizeSpinner( t, map, true );
            
            else if( HIWORD( wParam ) == EN_CHANGE && LOWORD( wParam ) == IDC_EXPORTHEIGHT )
                IClampTexSizeSpinner( t, map, false );
            
            else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_BM_CROP_IMAGE)
            {
                IParamBlock2 *pblock = map->GetParamBlock();
                PBBitmap *pbbm = pblock->GetBitmap(kBmpBitmap, t);
                if (pbbm)
                {
                    if (!pbbm->bm)
                        pbbm->bm = TheManager->Load(&pbbm->bi);
                    
                    BMCropper *cropper = TRACKED_NEW BMCropper(pblock);
                    
                    pbbm->bm->Display("Specify Cropping/Placement", BMM_CN, FALSE, TRUE, cropper);
                }
                //              bm->DeleteThis();
                return TRUE;
            }
            else if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_LAYER_RELOAD)
            {
                // TEMP
                IParamBlock2 *pblock = map->GetParamBlock();
                PBBitmap *pbbm = pblock->GetBitmap(kBmpBitmap, t);
                if (pbbm)
                {
                    plLayerTex *layer = (plLayerTex*)map->GetParamBlock()->GetOwner();

                    layer->RefreshBitmaps();
                    
                    layer->fMtlParams->MtlChanged();
                    layer->IChanged();
                }
                return TRUE;
            }
            else if (LOWORD(wParam) == IDC_LAYER_NAME)
            {
                plPlasmaMAXLayer *layer = (plPlasmaMAXLayer *)map->GetParamBlock()->GetOwner();
                if (layer == nil)
                    return FALSE;
                BOOL selectedNewBitmap = layer->HandleBitmapSelection();
                
                if(selectedNewBitmap)
                {
                    IParamBlock2 *pblock = map->GetParamBlock();
                    //plLayerTex *layer = (plLayerTex*)map->GetParamBlock()->GetOwner();
                    
                    //layer->SetBitmap(&bi);
                    //layer->IChanged();
                    //BitmapInfo *bi = &layer->GetPBBitmap()->bi;
                    
                    bmSelectBtn = GetICustButton(GetDlgItem(hWnd,IDC_LAYER_NAME));
                    PBBitmap *pbbm = layer->GetPBBitmap();
                    bmSelectBtn->SetText(pbbm != nil ? (TCHAR*)pbbm->bi.Filename() : "");
                    ReleaseICustButton(bmSelectBtn);
                    
                    if (pbbm != nil)
                    {
                        // Init values for clamping spinners to powers of 2
                        int width = IFloorPow2( pbbm->bi.Width() );
                        map->SetRange( kBmpExportWidth, 4.f, (float)width );
                        
                        int height = IFloorPow2( pbbm->bi.Height() );
                        map->SetRange( kBmpExportHeight, 4.f, (float)height );
                        
                        if( width > 512 )
                        {
                            height = (int)( 512.f * (float)( (float)height / (float)width ) );
                            width = 512;
                        }
                        else if( height > 512 )
                        {
                            width = (int)( 512.f * (float)( (float)width / (float)height ) );
                            height = 512;
                        }
                        pblock->SetValue( kBmpExportWidth, t, width );
                        pblock->SetValue( kBmpExportLastWidth, t, width );
                        pblock->SetValue( kBmpExportHeight, t, height );
                        pblock->SetValue( kBmpExportLastHeight, t, height );
                        
                        IClampTexSizeSpinner( t, map, true );
                    }   
                    return TRUE;
                }
                else
                {
                    return FALSE;
                }
            }
            break;
        }
        
        return FALSE;
    }
    virtual void DeleteThis() {};
    
    void    ISetDetailCurveNumLevels( IParamMap2 *map, TimeValue t )
    {
        /// Set the level count on the detail control
        plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( map->GetHWnd(), IDC_DETAIL_CURVE_CTRL );
        if( ctrl != NULL )
        {
            IParamBlock2 *pblock = map->GetParamBlock();
            int w = pblock->GetInt( kBmpExportWidth, t );
            int h = pblock->GetInt( kBmpExportHeight, t );
            int numLevels = 0;
            while( w > 1 && h > 1 )
            {
                w >>= 1;
                h >>= 1;
                numLevels++;
            }
            ctrl->SetNumLevels( numLevels );
        }
    }
    
    /// Clamp texture sizes to a power of 2
    void    IClampTexSizeSpinner( TimeValue t, IParamMap2 *map, bool clampWidth )
    {
        IParamBlock2 *pblock = map->GetParamBlock();
        ParamID     clampNew, clampOld;
        ParamID     otherNew, otherOld;
        
        if( clampWidth )
        {
            clampNew = kBmpExportWidth; clampOld = kBmpExportLastWidth;
            otherNew = kBmpExportHeight; otherOld = kBmpExportLastHeight;
        }
        else
        {
            clampNew = kBmpExportHeight; clampOld = kBmpExportLastHeight;
            otherNew = kBmpExportWidth; otherOld = kBmpExportLastWidth;
        }
        
        int     lastVal = pblock->GetInt( clampOld, t );
        int     tempVal, newVal = pblock->GetInt( clampNew, t );
        
        if( newVal < lastVal )
        {
            lastVal = newVal;
            for( tempVal = 1; tempVal <= newVal; tempVal <<= 1 );
            newVal = tempVal >> 1;
        }
        else
        {
            lastVal = newVal;
            for( tempVal = 1; tempVal < newVal; tempVal <<= 1 );
            newVal = tempVal;
        }
        
        pblock->SetValue( clampNew, t, newVal );
        pblock->SetValue( clampOld, t, newVal );
        
        // And clamp aspect ratio
        PBBitmap        *pbbm = pblock->GetBitmap( kBmpBitmap, t );
        
        if( pbbm != NULL )
        {
            int realWidth = pbbm->bi.Width();
            int realHeight = pbbm->bi.Height();
            
            float aspect;
            if( clampWidth )            
                aspect = (float)realHeight / (float)realWidth;
            else
                aspect = (float)realWidth / (float)realHeight;
            
            int value = newVal;
            value *= aspect;
            
            if( value < 4 )
            {
                // Can't be below 4!
                value = 4;
                pblock->SetValue( otherNew, t, value );
                pblock->SetValue( otherOld, t, value );
                value = value / aspect;
                pblock->SetValue( clampNew, t, value );
                pblock->SetValue( clampOld, t, value );
            }
            else
            {
                pblock->SetValue( otherNew, t, value );
                pblock->SetValue( otherOld, t, value );
            }
        }
        
        ISetDetailCurveNumLevels( map, t );
    }
    
    int     IFloorPow2( int value )
    {
        int     v;
        
        
        for( v = 1; v <= value; v <<= 1 );
        return v >> 1;
    }
    
};

static BitmapDlgProc gBitmapDlgProc;

static ParamBlockDesc2 gBitmapParamBlk
(
    plLayerTex::kBlkBitmap, _T("bitmap"),  0, GetLayerTexDesc(),//NULL,
    P_AUTO_CONSTRUCT + P_AUTO_UI, plLayerTex::kRefBitmap,

    IDD_LAYER_TEX, IDS_LAYER_TEX, 0, 0, &gBitmapDlgProc,

    // Bitmap
    kBmpUseBitmap,      _T("useBitmap"),    TYPE_BOOL,      0, 0,
        p_default,      TRUE,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_USE_BITMAP,
        end,
    kBmpBitmap,         _T("bitmap"),       TYPE_BITMAP,    P_SHORT_LABELS, 0,
        p_accessor,     &bmtex_accessor,
        end,

    // Crop/Place
    kBmpApply,          _T("apply"),    TYPE_BOOL,      0, 0,
        p_default,      FALSE,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_BM_CLIP,
        end,
    kBmpCropPlace,      _T("cropPlace"), TYPE_INT,      0, 0,
        p_default,      0,
        p_range,        0,  1,
        p_ui,           TYPE_RADIO, 2,  IDC_BM_CROP,IDC_BM_PLACE,
        end,
    kBmpClipU,          _T("clipU"),    TYPE_FLOAT,     P_ANIMATABLE, IDS_BITMAP_CLIPU,
        p_default,      0.0,
        p_range,        0.0, 1.0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_X, IDC_CLIP_XSPIN, 0.001f,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpClipV,          _T("clipV"),    TYPE_FLOAT,     P_ANIMATABLE, IDS_BITMAP_CLIPV,
        p_default,      0.0,
        p_range,        0.0, 1.0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_Y, IDC_CLIP_YSPIN, 0.001f,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpClipW,          _T("clipW"),    TYPE_FLOAT,     P_ANIMATABLE, IDS_BITMAP_CLIPW,
        p_default,      1.0,
        p_range,        0.0, 1.0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_W, IDC_CLIP_WSPIN, 0.001f,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpClipH,          _T("clipH"),    TYPE_FLOAT,     P_ANIMATABLE, IDS_BITMAP_CLIPH,
        p_default,      1.0,
        p_range,        0.0, 1.0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_CLIP_H, IDC_CLIP_HSPIN, 0.001f,
        p_accessor,     &bmtex_accessor,
        end,

    // Texture Color/Alpha
    kBmpDiscardColor,   _T("discardColor"), TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_BLEND_NO_COLOR,
        end,
    kBmpInvertColor,    _T("invertColor"),  TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_BLEND_INV_COLOR,
        end,
    kBmpDiscardAlpha,   _T("discardAlpha"), TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_DISCARD_ALPHA,
        end,
    kBmpInvertAlpha,    _T("invertAlpha"),  TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_BLEND_INV_ALPHA,
        end,

    // Texture Quality
    kBmpNonCompressed,  _T("nonCompressed"),TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_FORCE_NONCOMPRESSED,
        end,
    kBmpScaling,        _T("scaling"),      TYPE_INT,       0, 0,
        p_ui,           TYPE_RADIO, 3, IDC_SCALE_ALL, IDC_SCALE_HALF, IDC_SCALE_NONE,
        end,

    // Max Only
    kBmpMonoOutput,     _T("monoOutput"),   TYPE_INT,       0, 0,
        p_ui,           TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT, IDC_HSMAX_LAYER_ALPHAOUT,
        end,
    kBmpRGBOutput,      _T("rgbOutput"),    TYPE_INT,       0, 0,
        p_ui,           TYPE_RADIO, 2, IDC_HSMAX_LAYER_RGBOUT2, IDC_HSMAX_LAYER_ALPHAOUT2,
        end,

    // Mipmap
    kBmpNoFilter,       _T("noFilter"), TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_NO_FILTERING,
        end,
    kBmpMipBlur,        _T("mipBlur"),      TYPE_FLOAT,     0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_MIPBLUR_EDIT, IDC_MIPBLUR_SPIN, 0.4,
        p_range,        0.01f, 100.0f,
        p_default,      1.0,
        end,
    kBmpMipBias,        _T("mipBias"),      TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_USE_MIPBIAS,
        p_enable_ctrls, 1, kBmpMipBiasAmt,
        end,
    kBmpMipBiasAmt,     _T("mipBiasAmt"),   TYPE_FLOAT,     0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_FLOAT, IDC_MIPBIAS_EDIT, IDC_MIPBIAS_SPIN, 0.7,
        p_range,        -100.0, 100.0,
        p_default,      1.0,
        end,

    // Detail
    kBmpUseDetail,      _T("useDetail"),    TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_USE_DETAIL,
        p_default,      FALSE,
        p_enable_ctrls, 4, kBmpDetailStartSize, kBmpDetailStopSize, kBmpDetailStartOpac, kBmpDetailStopOpac,
        p_accessor,     &bmtex_accessor,
        end,

    kBmpDetailStartSize,_T("dropOffStart"), TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_SIZE_EDIT, IDC_DETAIL_START_SIZE_SPIN, 0.4,
        p_range,        0, 100,
        p_default,      0,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpDetailStopSize, _T("dropOffStop"),  TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_SIZE_EDIT, IDC_DETAIL_STOP_SIZE_SPIN, 0.4,
        p_range,        0, 100,
        p_default,      100,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpDetailStartOpac,    _T("detailMax"),    TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_START_OPAC_EDIT, IDC_DETAIL_START_OPAC_SPIN, 0.4,
        p_range,        0, 100,
        p_default,      100,
        p_accessor,     &bmtex_accessor,
        end,
    kBmpDetailStopOpac, _T("detailMin"),    TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_DETAIL_STOP_OPAC_EDIT, IDC_DETAIL_STOP_OPAC_SPIN, 0.4,
        p_range,        0, 100,
        p_default,      0,
        p_accessor,     &bmtex_accessor,
        end,

    kBmpExportWidth,    _T("exportWidth"),  TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTWIDTH, IDC_EXPORTWIDTH_SPINNER, SPIN_AUTOSCALE,
        p_range,        4, 2048,
        p_default,      512,
        end,
    kBmpExportHeight,   _T("exportHeight"), TYPE_INT,   0, 0,
        p_ui,           TYPE_SPINNER, EDITTYPE_INT, IDC_EXPORTHEIGHT, IDC_EXPORTHEIGHT_SPINNER, SPIN_AUTOSCALE,
        p_range,        4, 2048,
        p_default,      512,
        end,
    kBmpExportLastWidth,    _T("lastExportWidth"),  TYPE_INT,       0, 0,
        end,
    kBmpExportLastHeight,   _T("lastExportHeight"), TYPE_INT,       0, 0,
        end,

    // Keep a sysmem copy at runtime (for image examination/manipulation).
    kBmpNoDiscard,      _T("noDiscard"),    TYPE_BOOL,      0, 0,
        p_ui,           TYPE_SINGLECHEKBOX, IDC_NO_DISCARD,
        p_default,      FALSE,
        end,

    end
);
ParamBlockDesc2 *GetBitmapBlk() { return &gBitmapParamBlk; }

class BMTexPBAccessor : public PBAccessor
{
public:
    void Set(PB2Value& val, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
    {
      plLayerTex* layer = (plLayerTex*)owner;

      if(layer == NULL) return;
      
      IParamBlock2 *pb = layer->GetParamBlockByID(plLayerTex::kBlkBitmap);

        switch (id)
        {
            case kBmpBitmap:
                if (pb->GetMap())
                    pb->GetMap()->Invalidate(kBmpBitmap);

                // Update the bitmap saved by the layer
                //layer->SetBitmap(&val.bm->bi, tabIndex);
                break;

/*
            case kBmpFilename:
                bmt->SetMapName(val.s);
                break;

            case kBmpFiltering:
                bmt->filterType = val.i;
                if (bmt->thebm) 
                    bmt->thebm->SetFilter(bmFilterType(val.i));
                break;  
*/
            case kBmpClipU:
            {
                float u = val.f;
                float w = pb->GetFloat(kBmpClipW, t);
                if (u + w > 1.0f)
                {
                    pb->SetValue(kBmpClipW, t, 1.0f-u);
                    if (pb->GetMap())
                        pb->GetMap()->Invalidate(kBmpClipW);
                }
                break;
            }
            case kBmpClipW:
            {
                float w = val.f;
                float u = pb->GetFloat(kBmpClipU, t);
                if (u + w > 1.0f)
                {
                    pb->SetValue(kBmpClipU, t, 1.0f-w);
                    if (pb->GetMap())
                        pb->GetMap()->Invalidate(kBmpClipU);
                }
                break;
            }
            case kBmpClipV:
            {
                float v = val.f;
                float h = pb->GetFloat(kBmpClipH, t);
                if (v + h > 1.0f)
                {
                    pb->SetValue(kBmpClipH, t, 1.0f-v);
                    if (pb->GetMap())
                        pb->GetMap()->Invalidate(kBmpClipH);
                }
                break;
            }
            case kBmpClipH:
            {
                float h = val.f;
                float v = pb->GetFloat(kBmpClipV, t);
                if (v + h > 1.0f)
                {
                    pb->SetValue(kBmpClipV, t, 1.0f-h);
                    if (pb->GetMap())
                        pb->GetMap()->Invalidate(kBmpClipV);
                }
                break;
            }

            case kBmpDetailStartSize:
            case kBmpDetailStopSize:
            case kBmpDetailStartOpac:
            case kBmpDetailStopOpac:
                if( pb != NULL )
                {
                    if( IIsProcSettingDetailValues( pb ) )
                        break;  // Ignore, since we're the ones setting 'em

                    HWND dlg = pb->GetMap()->GetHWnd();
                    plDetailCurveCtrl *ctrl = GET_DETAIL_CURVE_CTRL( dlg, IDC_DETAIL_CURVE_CTRL );
                    if( ctrl != NULL )
                    {
                        if( id == kBmpDetailStartSize || id == kBmpDetailStartOpac )
                            ctrl->SetStartPoint( (float)pb->GetInt( kBmpDetailStartSize, t ) / 100.f,
                                                 (float)pb->GetInt( kBmpDetailStartOpac, t ) / 100.f );
                        else
                            ctrl->SetEndPoint(   (float)pb->GetInt( kBmpDetailStopSize, t ) / 100.f,
                                                 (float)pb->GetInt( kBmpDetailStopOpac, t ) / 100.f );
                    }

                    // Make sure start is less than end
                    if( id == kBmpDetailStartSize )
                    {
                        int end = pb->GetInt( kBmpDetailStopSize, t );
                        if( val.i > end )
                            pb->SetValue( kBmpDetailStopSize, t, val.i );
                    }
                    else if( id == kBmpDetailStopSize )
                    {
                        int start = pb->GetInt( kBmpDetailStartSize, t );
                        if( val.i < start )
                            pb->SetValue( kBmpDetailStartSize, t, val.i );
                    }

                }
                break;

            case kBmpUseDetail:
                if( pb != NULL )
                {
                    HWND dlg = pb->GetMap()->GetHWnd();
                    EnableWindow( GetDlgItem( dlg, IDC_DETAIL_CURVE_CTRL ), (BOOL)val.i );
                }
                break;
        }
    }
    void Get(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t, Interval &valid)
    {
    }

    // Gotta love hacks....
    bool    IIsProcSettingDetailValues( IParamBlock2 *pb );

};

static BMTexPBAccessor bmtex_accessor;


//=========================================================================================
// BMCropper
//=========================================================================================

void BMCropper::SetValues(float u, float v, float w, float h, BOOL md) 
{
    TimeValue t = GetCOREInterface()->GetTime();

    if (u != fPBlock->GetFloat(kBmpClipU, t))
    {
        fPBlock->SetValue(kBmpClipU, t, u);
        fPBlock->GetMap()->Invalidate(kBmpClipU);
    }

    if (v != fPBlock->GetFloat(kBmpClipV, t))
    {
        fPBlock->SetValue(kBmpClipV, t, v);
        fPBlock->GetMap()->Invalidate(kBmpClipV);
    }

    if (w != fPBlock->GetFloat(kBmpClipW, t))
    {
        fPBlock->SetValue(kBmpClipW, t, w);
        fPBlock->GetMap()->Invalidate(kBmpClipW);
    }

    if (h != fPBlock->GetFloat(kBmpClipH, t))
    {
        fPBlock->SetValue(kBmpClipH, t, h);
        fPBlock->GetMap()->Invalidate(kBmpClipH);
    }

    if (md != fPBlock->GetInt(kBmpCropPlace))
    {
        fPBlock->SetValue(kBmpCropPlace, t, md);
        fPBlock->GetMap()->Invalidate(kBmpCropPlace);
    }
}

void BMCropper::OnClose()
{
    delete this;
}


// Gotta love hacks....
bool    BMTexPBAccessor::IIsProcSettingDetailValues( IParamBlock2 *pb )
{
    BitmapDlgProc *proc = (BitmapDlgProc *)pb->GetMap()->GetUserDlgProc();
    if( proc != NULL )
        return proc->fSettingDetailValues;

    return false;
}

#endif