/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "editor/contedit.hh"
#include "controller.hh"
#include "map.hh"
#include "tile.hh"
#include "window/win_evt.hh"
#include "string/string.hh"
#include "draw_context.hh"
#include "font/font.hh"
#include "window/style.hh"
#include "resources.hh"
#include "editor/dialogs/obj_win.hh"
#include "math/pi.hh"
#include "mess_id.hh"
#include "loaders/load.hh"
#include "editor/edit_id.hh"
#include "g1_object.hh"
#include "editor/commands/fill.hh"
#include "device/keys.hh"
#include "editor/e_state.hh"
#include "window/win_evt.hh"
#include "m_flow.hh"
#include "image/color.hh"
#include "input.hh"

#include "editor/mode/e_tile.hh"
#include "editor/mode/e_camera.hh"
#include "editor/mode/e_light.hh"
#include "editor/mode/e_object.hh"
// #include "editor/mode/e_ai.hh"

#include "editor/editor.hh"
#include "r1_clip.hh"
#include "g1_render.hh"
#include "r1_api.hh"
#include "app/app.hh"
#include "window/wmanager.hh"
#include "f_tables.hh"
#include "map_vert.hh"
#include "lisp/lisp.hh"


void g1_controller_edit_class::update_cursor() 
{ 
  if (!active())
  {
    if (cursor_state!=G1_X_CURSOR)
    {
      cursor_state=G1_X_CURSOR;
      set_cursor(g1_resources.big_cursors[cursor_state]);
    }
  }
  else if (mode) 
    mode->update_cursor(); 
}



void g1_controller_edit_class::setup_context()
{  
  g1_context.window_setup(0,0,width(), height(),
                          g1_render.center_x, g1_render.center_y,
                          g1_render.scale_x, g1_render.scale_y,
                          g1_render.ooscale_x, g1_render.ooscale_y);
}

void g1_controller_edit_class::setup_mode()
{
  if (mode && active())
    mode->hide_focus();

  if (mode)
  {
    delete mode;   
  }

  mode=0;


  for (g1_mode_creator *mc=g1_mode_creator::first; mc; mc=mc->next)
    if (strcmp(g1_edit_state.major_mode, mc->name())==0)
      mode=mc->create_mode_handler(this);

  if (mode && active())
    mode->show_focus();

  refresh();


}

void g1_controller_edit_class::show_focus()
{
  if (!focus_visible)
  {
    mode->show_focus();
    focus_visible=i4_T;
  }
}

void g1_controller_edit_class::hide_focus()
{
  if (focus_visible)
  {
    mode->hide_focus();
    focus_visible=i4_F;
  }
}

void g1_controller_edit_class::refresh()
{
  i4_user_message_event_class ch(G1_MAP_CHANGED);
  i4_kernel.send_event(i4_current_app, &ch);
}

g1_controller_edit_class::~g1_controller_edit_class()
{  
  g1_edit_state.context_help.hide();

  if (active())
    unfocused();

  if (mode)
  {
    delete mode;   
    mode=0;
  }
}


void g1_controller_edit_class::unfocused()
{
  hide_focus();

  update_cursor();

  if (g1_current_controller.get()==this)
    g1_current_controller=0;
  

  request_redraw();
}

void g1_controller_edit_class::focused()
{
  setup_mode();

  g1_current_controller=this;
  
  i4_window_request_key_grab_class kgrab(this);
  i4_kernel.send_event(parent, &kgrab);

  show_focus();
  request_redraw();

  cursor_state=G1_DEFAULT_CURSOR;
  set_cursor(g1_resources.big_cursors[cursor_state]);
}

void g1_replace_cell(g1_map_class *map,
                     g1_map_cell_class &original, 
                     g1_map_cell_class &new_cell)
{
  g1_map_cell_class old;

  while (original.get_obj_list())
  {
    g1_object_class *o=original.object_list->object;
    o->unoccupy_location();

    old=original;
    original=new_cell;
    o->occupy_location();
    new_cell=original;
    original=old;        
  }

  old=original;
  original=new_cell;
  new_cell=old;
  
  I4_ASSERT(new_cell.object_list==0, "new cell list should be null");

}

void g1_controller_edit_class::replace_cell(g1_map_class *map,
                                            g1_map_cell_class &original, 
                                            g1_map_cell_class &new_cell)
{
  g1_replace_cell(map, original, new_cell);
}

void g1_controller_edit_class::restore_cell()
{
  if (need_restore_cell)
  {
    if (g1_map_is_loaded())
    {
      replace_cell(get_map(),
                   *get_map()->cell(cell_x, cell_y),
                   saved_cell);

      need_restore_cell=i4_F;

      li_call("redraw");
    }
  }
}

void g1_controller_edit_class::save_and_put_cell()
{
  if (g1_map_is_loaded())
  {
    saved_cell=*get_map()->cell(cell_x, cell_y);
    saved_cell.object_list=0;                    // don't keep the object list

    g1_map_cell_class new_cell;
    new_cell.init(g1_e_tile.get_cell_type(), 
                  g1_e_tile.get_cell_rotation(),
                  g1_e_tile.get_mirrored());

    replace_cell(get_map(), *get_map()->cell(cell_x, cell_y), new_cell);
  
    need_restore_cell=i4_T;

    li_call("redraw");
  }
}

class g1_map_fill_class : public g1_flood_fill_class
{

public:
  g1_map_class *map;
  g1_map_class *get_map() { return map; }
  w16 type, block_type;
  g1_rotation_type rotation;
  g1_player_type owner;
  i4_bool mirror;

  virtual void get_clip(sw32 &x1, sw32 &y1, sw32 &x2, sw32 &y2)
  {
    x1=y1=0;
    x2=get_map()->width()-1;
    y2=get_map()->height()-1;
  }

  i4_bool blocking(sw32 x, sw32 y)
  {
    w16 on_type=get_map()->cell(x,y)->type;
    if (on_type==block_type)
      return i4_F;
    else return i4_T;
  }

  void fill_block(sw32 x, sw32 y, sw32 startx, sw32 starty)
  { 
    g1_map_cell_class c;
    c.init(type, rotation, mirror);

    g1_replace_cell(map, *get_map()->cell(x,y), c);
  }

  g1_map_fill_class(g1_map_class *map, w16 type, 
                    g1_rotation_type rotation, 
                    i4_bool mirror,
                    g1_player_type owner,
                    w16 block_type)
    : map(map), type(type), rotation(rotation), 
      mirror(mirror),
      block_type(block_type), owner(owner)
  {}
};

void g1_controller_edit_class::fill_map()
{
  if (g1_map_is_loaded())
  {
    g1_editor_instance.add_undo(G1_MAP_CELLS);

    if (active())
      hide_focus();    


    w32 type_on=get_map()->cell(cell_x, cell_y)->type;

    if (type_on!=g1_e_tile.get_cell_type())
    {
      g1_map_fill_class f(get_map(), 
                          g1_e_tile.get_cell_type(),
                          g1_e_tile.get_cell_rotation(),
                          g1_e_tile.get_mirrored(),
                          g1_edit_state.current_team,
                          type_on);

      f.fill(cell_x, cell_y);
    }


    if (active())
      show_focus();

    changed();
  }
}

void g1_controller_edit_class::change_map_cell()
{
  g1_editor_instance.add_undo(G1_MAP_CELLS);

  graph_changed=i4_T;
  saved_cell.init(g1_e_tile.get_cell_type(), 
                  g1_e_tile.get_cell_rotation(),
                  g1_e_tile.get_mirrored());
  changed();
}


void g1_controller_edit_class::draw_3d_line(const i4_3d_point_class &p1,
                                            const i4_3d_point_class &p2,
                                            i4_color color1,
                                            i4_color color2,
                                            i4_image_class *local_image,
                                            g1_draw_context_class *context)
{
  g1_render.render_3d_line(p1, p2, color1, color2, context->transform);
}


void g1_controller_edit_class::draw_3d_point(sw32 w,
                                             i4_color color,
                                             const i4_3d_point_class &p,
                                             i4_image_class *local_image,
                                             g1_draw_context_class *context)
{
  r1_vert v;
  if (g1_render.project_point(p, v, context->transform))
    r1_clip_clear_area((sw32)v.px-w, (sw32)v.py-w, 
                       (sw32)v.px+w, (sw32)v.py+w, color, v.v.z, *context->context,
                       g1_render.r_api);
}


void g1_controller_edit_class::draw_spline(i4_image_class *local_image,
                                           g1_draw_context_class *g1_context, 
                                           i4_color cpoint_color,
                                           i4_color line_color,
                                           i4_color spline_color,
                                           i4_spline_class *s,
                                           w32 cur_frame)
{
  sw32 i;
  i4_float lx,ly,x,y,z;
  if (!s->total()) return;

  i4_spline_class::point *p;    
  i4_3d_point_class cur_p, last_p;

  for (i=0; itotal(); i++)
  {
    p=s->get_control_point(i);     

    cur_p=i4_3d_point_class(p->x, p->y, p->z);

    if (i!=0)
      draw_3d_line(cur_p, last_p, line_color, line_color, local_image, g1_context);
    
    last_p=cur_p;
  }


  p=s->get_control_point(0);

  w32 frame=0;

  while (p->next)
  {
    while (framenext->frame)
    {
      i4_float x1,y1,z1,x2,y2,z2;

      i4_3d_point_class q1,q2;
      s->get_point(frame, x1,y1,z1);
          

      if (g1_e_camera.should_show_frames())  
        draw_3d_point(1, 0x0000ff, i4_3d_point_class(x1,y1,z1), local_image, g1_context);

      if (frame==cur_frame)
        draw_3d_point(1, 0xff00ff, i4_3d_point_class(x1,y1,z1), local_image, g1_context);

      frame++;

      s->get_point(frame, x2,y2,z2);
      draw_3d_line(i4_3d_point_class(x1,y1,z1), 
                   i4_3d_point_class(x2,y2,z2),
                   spline_color, spline_color,
                   local_image, g1_context);


    }
    p=p->next;
  }

  for (i=0; itotal(); i++)
  {
    i4_3d_point_class q;

    i4_spline_class::point *p=s->get_control_point(i);

    if (p->selected)
      draw_3d_point(1, 0xffff00,
                    i4_3d_point_class(p->x,p->y,p->z), local_image, g1_context);
    else
      draw_3d_point(1, 
                    cpoint_color,
                    i4_3d_point_class(p->x,p->y,p->z), local_image, g1_context);
  }


}

void g1_controller_edit_class_tile_post_cell_draw(sw32 x, sw32 y, void *context)
{
  ((g1_controller_edit_class *)context)->tile_cell_draw(x,y);
}


void g1_controller_edit_class_object_post_cell_draw(sw32 x, sw32 y, void *context)
{
  ((g1_controller_edit_class *)context)->object_cell_draw(x,y);
}

void g1_controller_edit_class::object_cell_draw(sw32 x, sw32 y)
{
  g1_map_class *map=get_map();

   int bgrade=-1;
//   for (int j=0; jget_block_map(j);
//     if (bmap && bmap->is_blocked(x,y, G1_NORTH | G1_SOUTH | G1_WEST | G1_EAST))
//       bgrade=j;
//   }

  if ((map->cell(x,y)->flags & g1_map_cell_class::IS_GROUND)==0)
    bgrade=0;

  if (map->cell(x,y)->get_solid_list())
    bgrade=1;
  else
    bgrade=-1;

  if (bgrade!=-1)
  {
    w32 c[5]={0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff };

    float r=0.01;
    draw_3d_line(i4_3d_point_class(x,y, map->terrain_height(x,y)+r),
                 i4_3d_point_class(x+1,y+1, map->terrain_height(x+1,y+1)+r),
                 c[bgrade], c[bgrade], local_image, &g1_context);

    draw_3d_line(i4_3d_point_class(x+1,y, map->terrain_height(x+1,y)+r),
                 i4_3d_point_class(x,y+1, map->terrain_height(x,y+1)+r),
                 c[bgrade], c[bgrade], local_image, &g1_context);
  }

}

void g1_controller_edit_class::tile_cell_draw(sw32 x, sw32 y)
{
  r1_vert rv;
  g1_map_class *map=get_map();

  g1_map_vertex_class *v[4];
  v[0]=map->vertex(x,y);
  v[1]=v[0]+1;
  v[2]=v[0]+map->width()+1;
  v[3]=v[0]+map->width()+1+1;

  int vt[8]={x,y,x+1,y,x,y+1,x+1,y+1};

  object_cell_draw(x,y);

  for (w32 i=0; i<4; i++)
  {
    if (v[i]->is_selected())
    { 
      i4_3d_point_class cp(vt[i*2],vt[i*2+1],v[i]->get_height());

      if (g1_render.project_point(cp, rv, g1_context.transform))
        r1_clip_clear_area((sw32)rv.px-1, (sw32)rv.py-1, (sw32)rv.px+1, (sw32)rv.py+1, 
                           0xffff00, rv.v.z, *g1_context.context,
                           g1_render.r_api);
    }

    v[i]->set_flag(g1_map_vertex_class::WAS_DRAWN_LAST_FRAME,1);
  }

}

void g1_controller_edit_class::editor_pre_draw(i4_draw_context_class &context)
{
  g1_render.frame_ratio=0;

  if (g1_map_is_loaded() && active())
  {
    int w=(get_map()->width()+1) * (get_map()->height()+1);
    g1_map_vertex_class *v=get_map()->vertex(0,0);

    for (int i=0; iset_flag(g1_map_vertex_class::WAS_DRAWN_LAST_FRAME, 0);
  }

  if (strcmp(g1_edit_state.major_mode,"TILE")==0 && get_map())
    get_map()->set_post_cell_draw_function(g1_controller_edit_class_tile_post_cell_draw, this);

  if (strcmp(g1_edit_state.major_mode,"OBJECT")==0 && get_map())
    get_map()->set_post_cell_draw_function(g1_controller_edit_class_object_post_cell_draw, this);


  if (g1_resources.paused)
    g1_context.draw_editor_stuff=i4_T;

  if (view.get_view_mode()==G1_EDIT_MODE)
  {
    g1_render.r_api->set_write_mode(R1_WRITE_COLOR | R1_WRITE_W);
    r1_clip_clear_area(0,0,width()-1, height()-1, 0, r1_far_clip_z, context, g1_render.r_api);
    g1_render.r_api->set_write_mode(R1_WRITE_COLOR | R1_WRITE_W | R1_WRITE_COLOR);
  }

}

void g1_controller_edit_class::editor_post_draw(i4_draw_context_class &context)
{

  g1_context.draw_editor_stuff=i4_F;

  if (g1_map_is_loaded())
    get_map()->set_post_cell_draw_function(0,0);

  mode->post_draw(context);

  if (active())
    g1_render.draw_rectangle(0,0, width()-1, height()-1, 0xffff00, context);
  else
    g1_render.draw_rectangle(0,0, width()-1, height()-1, 0, context);
}



void g1_controller_edit_class::process_input(i4_time_class tick_time)
{
  /*  if (have_focus)
  {
    if (!get_map()) return;
   
    i4_angle a=camera.ground_rotate, move_speed=1.0, pan_speed=0.2;

    if (camera.gx>=0 && camera.gy>=0 && 
        camera.gxwidth() && camera.gyheight())
    {
      i4_float th=get_map()->terrain_height(camera.gx, camera.gy);
      i4_float camera_dist=camera.gz-th;

      if (camera_dist<6)
      {
        pan_speed*=camera_dist/6.0;
        move_speed*=camera_dist/6.0;
      }
    }



    if (g1_editor_instance.movement.rotate_left)
      rotate(0.08, 0);

    if (g1_editor_instance.movement.rotate_right)
      rotate(-0.08, 0);

    if (g1_editor_instance.movement.rotate_up)
      rotate(0, -0.08);

    if (g1_editor_instance.movement.rotate_down)
      rotate(0, 0.08);


    if (g1_editor_instance.movement.pan_down)
      pan(0,0,-pan_speed);

    if (g1_editor_instance.movement.pan_up)
      pan(0,0,pan_speed);

    if (g1_editor_instance.movement.pan_left)
      pan(-sin(a)*move_speed, cos(a)*move_speed,0);

    if (g1_editor_instance.movement.pan_right)
      pan(sin(a)*move_speed, -cos(a)*move_speed,0);

    if (g1_editor_instance.movement.pan_forward)
      pan(cos(a)*move_speed, sin(a)*move_speed,0);

    if (g1_editor_instance.movement.pan_backward)
      pan(-cos(a)*move_speed, -sin(a)*move_speed,0);


      } */
}

g1_map_vertex_class *g1_controller_edit_class::find_map_vertex(sw32 x, sw32 y,
                                                               sw32 &vx, sw32 &vy)
{
  sw32 sx,sy,ex,ey, tx,ty;
  sx=(sw32)0;  sy=(sw32)0;
  ex=(sw32)get_map()->width();  ey=(sw32)get_map()->height();

  g1_map_vertex_class *mv=get_map()->vertex(0,0);

  if (!g1_context.transform)
    return 0;

  // transform all the points in area and test proximity to mouse x,y
  for (ty=sy; ty<=ey; ty++)
    for (tx=sx; tx<=ex; tx++)
    {
      if (mv->get_flag(g1_map_vertex_class::WAS_DRAWN_LAST_FRAME))
      {
        r1_vert v;

        if (g1_render.project_point(i4_3d_point_class(tx, ty, mv->get_height()), 
                                    v, g1_context.transform))
        {
          if (abs((sw32)v.px-x)<=3 && abs((sw32)v.py-y)<=3)
          {
            vx=tx; vy=ty;
            return mv;
          }
        }      
      }
      mv++;
    }

  return 0;
}




void g1_controller_edit_class::clear_selected_verts()
{
  if (!i4_current_app->get_window_manager()->control_pressed())
  {
    i4_bool change=i4_F;

    sw32 w=(get_map()->width()+1)*(get_map()->height()+1);
    g1_map_vertex_class *v=get_map()->vertex(0,0);


    for (w32 x=0; xis_selected())
      {
        change=i4_T;
        v->set_is_selected(i4_F);
      }

    if (change)
    {
      changed();
      refresh();
    }
  }
}


void g1_controller_edit_class::select_verts_in_area(sw32 x1, sw32 y1, 
                                                    sw32 x2, sw32 y2, 
                                                    g1_mode_handler::select_modifier mod)
{
  sw32 w=(get_map()->width()+1),h=(get_map()->height()+1),x,y;
  g1_map_vertex_class *v=get_map()->vertex(0,0);
  
  i4_bool change=i4_F;

  g1_editor_instance.add_undo(G1_MAP_VERTS);


  for (y=0; yget_flag(g1_map_vertex_class::WAS_DRAWN_LAST_FRAME))
      {
        r1_vert vt;
        if (g1_render.project_point(i4_3d_point_class(x, y, v->get_height()),
                                    vt,
                                    g1_context.transform))
        {
          if (vt.px>=x1 && vt.px<=x2 && vt.py>=y1 && vt.py<=y2)
          {
            change=i4_T;

            if (mod==g1_mode_handler::SUB_FROM_OLD)
              v->set_is_selected(i4_F);
            else
              v->set_is_selected(i4_T);


          }
          else if (mod==g1_mode_handler::CLEAR_OLD_IF_NO_SELECTION)
          {
            if (v->is_selected())
            {
              change=i4_T;
              v->set_is_selected(i4_F);
            }
          }
        } else if (mod==g1_mode_handler::CLEAR_OLD_IF_NO_SELECTION)
        {
          if (v->is_selected())
          {
            change=i4_T;
            v->set_is_selected(i4_F);
          }
        }
      }
    }

  if (change)
  {
    changed();
    refresh();
  }
}


g1_controller_edit_class::g1_controller_edit_class(w16 w, w16 h,
                                                   i4_graphical_style_class *style)
  : g1_object_controller_class(w,h, style)
{    
  focus_visible=i4_F;

  move_vert_x=move_vert_y=0;
  move_vert_z=0;
  move_verts=i4_F;

  edit_win=0;
  drag_select=i4_F;
  move_points=i4_F;

  graph_changed=i4_F;
  need_restore_cell=i4_F;     
  place_object=0;
  selected_object=0;

  place_object_on_map=i4_F;
  cell_x=cell_y=0;
    
  mode=0;
  setup_mode();

}