#include "dk_menup.h"
#include "dk_model.h"
#include "dk_misc.h"
#include "client.h"

extern model_t *box_model;

CInterfaceControl::CInterfaceControl() {

}

CInterfaceControl::~CInterfaceControl() {

}

void CInterfaceControl::Reset() {
    
}

void CInterfaceControl::Animate(int elapsed_time) {

}

//lets the control draw any 2d artwork that will be behind the 3d models.
void CInterfaceControl::DrawBackground() {

}

//places the 3d models.
void CInterfaceControl::PlaceEntities() {

}

//lets the control draw any 2d artwork that will be in front of the 3d models.
void CInterfaceControl::DrawForground() {

}

bool CInterfaceControl::MousePos(int x, int y) {
    return false;
}

//tells the control that the mouse is being pressed or released.
bool CInterfaceControl::MouseUp(int x, int y) {
    return false;
}

bool CInterfaceControl::MouseDown(int x, int y) {
    return false;
}


CInterfaceButton::CInterfaceButton() {
    x = y = -100;
    w = h = 1;

    under_cursor = false;
    have_capture = false;
    state = IBS_UP;

    skin = NULL;
}

CInterfaceButton::~CInterfaceButton() {

}

void CInterfaceButton::Reset() {
    have_capture = false;
    under_cursor = false;

    state = IBS_UP;
    animation_time = 0;
}

//positions the button on the screen and resets it's state.
void CInterfaceButton::Init(int x, int y, int w, int h, const char *skin) {
    this->x = x;
    this->y = y;
    this->w = w;
    this->h = h;

    if (skin == NULL) {
        skin = "/models/interface/unitbox/buttonmini.bmp";
    }

    this->skin = skin;

    Reset();
}

void CInterfaceButton::Animate(int elapsed_time) {
    //check our state.
    if (state == IBS_UP || state == IBS_DOWN) return;

    //we are animating.
    //add the elapsed time to our total.
    animation_time += elapsed_time;

    //check if we are done animating.
    if (animation_time >= IB_ANIMATION_TIME) {
        //check our state.
        if (state == IBS_GOING_UP) {
            //we are up now.
            state = IBS_UP;
        }
        else if (state == IBS_GOING_DOWN) {
            //we are down now.
            state = IBS_DOWN;
        }
    }
}

#define IB_GROW_DOWN 5.0f
#define IB_GROW_UNDER_CURSOR 1.0f

void CInterfaceButton::PlaceEntities() {
    //get an entity.
    entity_t *button = NewEntity();

    //make the button fullbright.
    button->flags = RF_FULLBRIGHT;

    button->model = box_model;
    button->skin = re.RegisterSkin((char *)skin, RESOURCE_INTERFACE);

    //set the origin
    button->origin[0] = 879.0f;
    button->origin[1] = 320 - x - w * 0.5f;
    button->origin[2] = 240 - y - h * 0.5f;

    button->angles[0] = 0;
    button->angles[1] = 0;
    button->angles[2] = 0;

    //we grow/shrink the button depending on its state.
    float button_grow = 0.0f;
    if (state == IBS_UP) {
        if (under_cursor == true) button_grow = IB_GROW_UNDER_CURSOR;
        else button_grow = 0.0f;
    }
    else if (state == IBS_DOWN) {
        button_grow = IB_GROW_DOWN;
    }
    else if (state == IBS_GOING_UP) {
        //get our progress in the animation.
        float percent = animation_time / float(IB_ANIMATION_TIME);
        
        button_grow = IB_GROW_DOWN - (IB_GROW_DOWN) * percent;
    }
    else if (state == IBS_GOING_DOWN) {
        //get our progress in the animation.
        float percent = animation_time / float(IB_ANIMATION_TIME);
        
        button_grow = (IB_GROW_DOWN - IB_GROW_UNDER_CURSOR) * percent + IB_GROW_UNDER_CURSOR;
    }

    button->render_scale[0] = 1;
    button->render_scale[1] = w + button_grow;
    button->render_scale[2] = h + button_grow;

    button->frame = 0;
}

bool CInterfaceButton::MousePos(int x, int y) {
    bool ret = false;

    if (x < this->x || x > this->x + w || y < this->y || y > this->y + h) {
        //we are off the button.
        under_cursor = false;
        ret = false;

        //if we have capture, we are down and should be going up now.
        if (have_capture == true) {
            Up();
        }
    }
    else {
        if (under_cursor == false) {
            S_StartLocalSound("menus/button_003.wav");
        }

        //we are on the button.
        under_cursor = true;
        ret = true;

        //if we have capture, make sure the button is down.
        if (have_capture == true) {
            Down();
        }
    }

    //if we have the capture, we return true no matter what.
    if (have_capture == true) {
        ret = true;
    }

    return ret;
}

bool CInterfaceButton::MouseUp(int x, int y) {
    //if we dont have the capture, do nothing.
    if (have_capture == false) return false;

    //we go to our up state no matter if the cursor was on us or not.
    Up();

    //we lose capture.
    have_capture = false;

    //we have capture, check if the mouse is still over us.
    if (x < this->x || x > this->x + w || y < this->y || y > this->y + h) {
        //not over us.
        return false;
    }

    //the mouse was over us.  we were pressed.
    return true;
}

bool CInterfaceButton::MouseDown(int x, int y) {
    if (x < this->x || x > this->x + w) {
        //we are off the button.
        return false;
    }
    else if (y < this->y || y > this->y + h) {
        //we are off the button.
        return false;
    }

    //we are on the button.

    //capture the mouse.
    have_capture = true;
    
    //animate to the down state.
    Down();

    return true;
}

void CInterfaceButton::Down() {
    //check our state.
    if (state == IBS_UP || state == IBS_GOING_UP) {
        //change to going down state.
        state = IBS_GOING_DOWN;

        //reset our time.
        animation_time = 0;
    }
}

void CInterfaceButton::Up() {
    //check our state.
    if (state == IBS_DOWN || state == IBS_GOING_DOWN) {
        //change to going up state.
        state = IBS_GOING_UP;

        //reset our time,
        animation_time = 0;
    }
}

///////////////////////////////////////////////////////////////////////////
///  CInterfaceBox
///
///  Used to draw a border around area of screen.
///////////////////////////////////////////////////////////////////////////

void CInterfaceBox::Init(int l, int t, int r, int b, int thick) {
    left = l;
    right = r;
    top = t;
    bottom = b;
    thickness = thick;
}

void CInterfaceBox::PlaceEntities() {
    //compute half our thickness.
    float half = thickness * 0.5f;

    entity_t *corner;
    entity_t *line;

    //make the corner entities.
    //top-left.
    corner = NewEntity();
    corner->flags = RF_FULLBRIGHT;
    corner->model = box_model;
    corner->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    corner->origin[0] = 879.0f;
    corner->origin[1] = 320 - left - half;
    corner->origin[2] = 240 - top - half;
    corner->angles[0] = 0; corner->angles[1] = 0; corner->angles[2] = 0;
    corner->render_scale[0] = 1.0f;
    corner->render_scale[1] = thickness;
    corner->render_scale[2] = thickness;
    corner->frame = 0;
  
    //top-right
    corner = NewEntity();
    corner->flags = RF_FULLBRIGHT;
    corner->model = box_model;
    corner->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    corner->origin[0] = 879.0f;
    corner->origin[1] = 320 - right + half;
    corner->origin[2] = 240 - top - half;
    corner->angles[0] = 0; corner->angles[1] = 0; corner->angles[2] = 270;
    corner->render_scale[0] = 1.0f;
    corner->render_scale[1] = thickness;
    corner->render_scale[2] = thickness;
    corner->frame = 0;

    //bottom-right
    corner = NewEntity();
    corner->flags = RF_FULLBRIGHT;
    corner->model = box_model;
    corner->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    corner->origin[0] = 879.0f;
    corner->origin[1] = 320 - right + half;
    corner->origin[2] = 240 - bottom + half;
    corner->angles[0] = 0; corner->angles[1] = 0; corner->angles[2] = 180;
    corner->render_scale[0] = 1.0f;
    corner->render_scale[1] = thickness;
    corner->render_scale[2] = thickness;
    corner->frame = 0;
  
    //bottom-left
    corner = NewEntity();
    corner->flags = RF_FULLBRIGHT;
    corner->model = box_model;
    corner->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    corner->origin[0] = 879.0f;
    corner->origin[1] = 320 - left - half;
    corner->origin[2] = 240 - bottom + half;
    corner->angles[0] = 0; corner->angles[1] = 0; corner->angles[2] = 90;
    corner->render_scale[0] = 1.0f;
    corner->render_scale[1] = thickness;
    corner->render_scale[2] = thickness;
    corner->frame = 0;

    //draw the horizontal and vertical lines.
    //left
    line = NewEntity();
    line->flags = RF_FULLBRIGHT;
    line->model = box_model;
    line->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    line->origin[0] = 879.0f;
    line->origin[1] = 320 - left - half;
    line->origin[2] = 240 - top - (bottom - top) * 0.5f;
    line->angles[0] = 0; line->angles[1] = 0; line->angles[2] = 90;
    line->render_scale[0] = 1.0f;
    line->render_scale[1] = (bottom - top) - 2.0f * thickness;
    line->render_scale[2] = thickness;

    //top
    line = NewEntity();
    line->flags = RF_FULLBRIGHT;
    line->model = box_model;
    line->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    line->origin[0] = 879.0f;
    line->origin[1] = 320 - left - (right - left) * 0.5f;
    line->origin[2] = 240 - top - half;
    line->angles[0] = 0; line->angles[1] = 0; line->angles[2] = 0;
    line->render_scale[0] = 1.0f;
    line->render_scale[1] = (right - left) - 2.0f * thickness;
    line->render_scale[2] = thickness;

    //right
    line = NewEntity();
    line->flags = RF_FULLBRIGHT;
    line->model = box_model;
    line->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    line->origin[0] = 879.0f;
    line->origin[1] = 320 - right + half;
    line->origin[2] = 240 - top - (bottom - top) * 0.5f;
    line->angles[0] = 0; line->angles[1] = 0; line->angles[2] = 270;
    line->render_scale[0] = 1.0f;
    line->render_scale[1] = (bottom - top) - 2.0f * thickness;
    line->render_scale[2] = thickness;

    //bottom
    line = NewEntity();
    line->flags = RF_FULLBRIGHT;
    line->model = box_model;
    line->skin = re.RegisterSkin("/models/interface/unitbox/buttonmini.bmp", RESOURCE_INTERFACE);
    line->origin[0] = 879.0f;
    line->origin[1] = 320 - left - (right - left) * 0.5f;
    line->origin[2] = 240 - bottom + half;
    line->angles[0] = 0; line->angles[1] = 0; line->angles[2] = 180;
    line->render_scale[0] = 1.0f;
    line->render_scale[1] = (right - left) - 2.0f * thickness;
    line->render_scale[2] = thickness;
}


///////////////////////////////////////////////////////////////////////////
///
///  CInterfaceSlider
///
///////////////////////////////////////////////////////////////////////////

void CInterfaceSlider::Init(int x, int y, int w, int h) {
    //set our position info.
    this->x = x;
    this->y = y;
    this->w = w;
    this->h = h;

    have_capture = false;

    for (int i = 0; i < SLIDER_MAX_STEPS; i++) {
        tick_pos[i] = 0.0f;
    }
}

void CInterfaceSlider::InitLinear(int steps, float low, float high) {
    //reset our state.
    position = 0;

    //we are not log scale.
    log_scale = false;

    //set our function vars.
    this->steps = steps;
    this->low = low;
    this->high = high;
}

void CInterfaceSlider::InitExp(int steps, float low, float high) {
    //reset our state.
    position = 0;

    //we are log scale.
    log_scale = true;

    //set our function vars.
    this->steps = steps;
    this->low = low;
    this->high = high;
}

void CInterfaceSlider::Animate(int elapsed_time) {
    //the amount of time we use to animate the ticks going up.
    int time = elapsed_time;

    //go left to right and animate the ticks going up.
    for (int i = 0; i <= position; i++) {
        //first, compute the amount of time we will give to the next tick.
        int next_tick_time = time;

        //check if we are over the threshold to allow the next tick to move.
        if (tick_pos[i] < SLIDER_PROPAGATE_THRESH) {
            //we need to subtract the amount of time we will take to reach the threshold.
            next_tick_time = int(time - (SLIDER_PROPAGATE_THRESH - tick_pos[i]) * SLIDER_FLIP_TIME);
        }

        if (tick_pos[i] == 0.0f) {
            //make a sound
//            S_StartLocalSound("menus/button_003.wav");
        }

        //compute the new position for this tick.
        tick_pos[i] += time / SLIDER_FLIP_TIME;
        //clamp it to 1.0
        bound_max(tick_pos[i], 1.0f);

        //set our elapsed time to the time our next tick gets.
        time = next_tick_time;

        //check if we have any time left.
        if (time <= 0) break;
    }

    //reset the time.
    time = elapsed_time;

    //go right to left and animate the ticks going down
    for (i = steps - 1; i > position; i--) {
        //compute the amount of time we will give to the next tick.
        int next_tick_time = time;

        //check if we are over the threshold to allow the next tick.
        if (tick_pos[i] > 1.0f - SLIDER_PROPAGATE_THRESH) {
            //we subtract the amount of time we will take to reach the threshold.
            next_tick_time = int(time - (tick_pos[i] - (1.0f - SLIDER_PROPAGATE_THRESH)) * SLIDER_FLIP_TIME);
        }

        if (tick_pos[i] == 1.0f) {
            //make a sound
//            S_StartLocalSound("menus/button_002.wav");
        }

        //compute the new position for this tick.
        tick_pos[i] -= time / SLIDER_FLIP_TIME;
        //clamp to 0
        bound_min(tick_pos[i], 0.0f);

        //set our elapsed time for the next tick.
        time = next_tick_time;

        //check if we have any time left.
        if (time <= 0) break;
    }
}

void CInterfaceSlider::PlaceEntities() {
    //the width of the tick mark.
    float width = w / float(steps);
    //the z coordinate of the middle of the ticks.
    float z = 240 - y - h * 0.5f;

    //the skin of our tick mark.
    struct image_s *skin = re.RegisterSkin("/models/interface/unitbox/buttonled.bmp", RESOURCE_INTERFACE);

    //make all our tick marks.
    for (int i = 0; i < steps; i++) {
        //get the left and right of the tick.
        float left = i * width + x;
        float right = (i + 1) * width + x;

        //the height of the tick mark.
        float height = h * (float(i) / steps * 0.0f + 1.0f);

        //get an entity.
        entity_t *tick = NewEntity();

        //set flags.
        tick->flags = RF_FULLBRIGHT;

        //set model and skin
        tick->model = box_model;
        tick->skin = skin;

        //set the origin.
        tick->origin[0] = 879.0f;
        tick->origin[1] = 320 - left - width * 0.5f;
        tick->origin[2] = z;

        //set the size.
        tick->render_scale[0] = height;
        tick->render_scale[1] = width;
        tick->render_scale[2] = 1.0f;

        //set the rotation.
        tick->angles[0] = 90.0f * tick_pos[i];
        tick->angles[1] = tick->angles[2] = 0.0f;
    }
}

bool CInterfaceSlider::MousePos(int x, int y) {
    //check if we have the mouse capture.
    if (have_capture == true) {
        //get the tick that corresponds to this x value.
        int x_diff = x - this->x;
        float percent = x_diff / float(w);
        int tick = percent * steps;

        //clamp it.
        bound_min(tick, 0);
        bound_max(tick, steps - 1);

        position = tick;

        return true;
    }

    return false;
}

bool CInterfaceSlider::MouseDown(int x, int y) {
    //check if the position is on us.
    if (x < this->x || y < this->y || x > this->x + w || y > this->y + h) {
        //the mouse is not on us.
        return false;
    }

    //the user clicked the mouse down on us.
    have_capture = true;

    //move the slider position to where they clicked.
    MousePos(x, y);

    return true;
}

//a return value of true means this button was pressed by the user.
bool CInterfaceSlider::MouseUp(int x, int y) {
    //check if we have the capture.
    if (have_capture == true) {
        //release capture.
        have_capture = false;
        return true;
    }

    return false;
}

float CInterfaceSlider::Position() {
    //interpolate between low and high.
    float pos = low + (high - low) * (position / float(steps - 1));

    if (log_scale == true) {
        return pow(10, pos);
    }
    else {
        return pos;
    }
}

void CInterfaceSlider::Position(float pos) {
    //check if we are log or not.
    if (log_scale == true) {
        //check the value.
        if (pos < 0.00001f) {
            pos = 0.00001f;
        }

        //take the log of the value.
        float mag = log10(pos);

        //get the slider position.
        position = int((mag - low)/(high - low) * (steps - 1));
    }
    else {
        //get the slider position.
        position = int((pos - low)/(high - low) * (steps - 1));
    }

    //error check the new position.
    bound_min(position, 0);
    bound_max(position, steps - 1);

    //move all the tick marks to the correct position.
    for (int i = 0; i < steps; i++) {
        if (i <= position) {
            //this tick goes up.
            tick_pos[i] = 1.0f;
        }
        else {
            //this tick goes down.
            tick_pos[i] = 0.0f;
        }
    }
}

