/**********************************************************************
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/dialogs/path_win.hh"
#include "window/window.hh"
#include "window/style.hh"
#include "gui/button.hh"
#include "gui/image_win.hh"
#include "gui/butbox.hh"

#include "map.hh"
#include "path_api.hh"
#include "critical_graph.hh"
#include "critical_map.hh"
#include "solvemap_astar.hh"
#include "solvegraph_breadth.hh"

#include "image/depth.hh"
#include "image/image32.hh"
#include "device/keys.hh"

#include "editor/editor.hh"

enum {
  P1_SET_START,
  P1_SET_DESTINATION,
  P1_SET_CRITICAL,
  P1_LOAD_HOTSPOTS,

  P1_SET_GRADE1,
  P1_SET_GRADE2,
  P1_SET_GRADE3,
  P1_SET_GRADE4,

  P1_SET_SIZE1,
  P1_SET_SIZE3,

  P1_RELOAD_MAP,

  P1_SET_BLOCK,
  P1_LAY_OBJECT,

  P1_STOP,
  P1_STEP,

  P1_UNSET_BLOCK_EVENT
};




g1_path_tool_window_class::g1_path_tool_window_class(i4_graphical_style_class *style, 
                                                     i4_event_handler_class *send_to,
                                                     int buttons, i4_image_class **img, 
                                                     i4_const_str **help_names)
  : i4_button_box_class(send_to, i4_F)
//{{{
{
  int x=0,y=0,ny=0;
  for (int i=0; iset_popup(i4_T);

      if (x+b->width()>40)
      {
        x=0;
        y+=ny;
        ny=0;
      }
      add_button(x,y, b);
      x += b->width();
      ny = b->height()>ny ? b->height() : ny;
    }
  }
    
  resize_to_fit_children();
}
//}}}


g1_path_window_class::g1_path_window_class(g1_map_class *map, 
                                           i4_image_class **icons)
  : map(map), 
    critical_graph(map->get_critical_graph()),
    i4_parent_window_class(map->width()*CELL_SIZE,map->height()*CELL_SIZE)
//{{{
{
  start_icon = icons[0];
  dest_icon = icons[1];
  crit_icon = icons[2];
  
  bitmap=0;
  map_changed = 1;

  grade = 0;
  size = 2;
  tofrom = 0;
  points = 0;
  maker = new g1_critical_map_maker_class();
  maker->make_criticals(map, critical_graph);
  solvemap = new g1_astar_map_solver_class(map->get_block_map(grade));
  solvegraph = new g1_breadth_first_graph_solver_class(critical_graph);
  solve();
}
//}}}

g1_path_window_class::~g1_path_window_class()
//{{{
{
  if (maker)
    delete maker;
  if (solvemap)
    delete solvemap;
  if (solvegraph)
    delete solvegraph;
  if (bitmap)
    delete bitmap;
}
//}}}

w32 g1_path_window_class::critical_color(w16 x, w16 y)
//{{{
{
  w32 crit_col[6] = { 0x200000, 0x002000, 0x000020,
                      0x400000, 0x004000, 0x000040 };
  w32 col=0;
  w16 crit;

  crit = map->cell(x,y).nearest_critical[grade][tofrom][0];
    
  for (int i=0; i<6; i++)
    if (crit & (1<cell(start.x,start.y).nearest_critical[grade][tofrom][0];
  g1_graph_node dest_node = map->cell(dest.x,dest.y).nearest_critical[grade][tofrom][0];

  solvemap->path_solve(start.x, start.y, dest.x, dest.y, size, size, grade, point, points);
  solvegraph->path_solve(start_node, dest_node, size, grade, point, points);

  g1_solvemap = solvemap; //(OLI) Debug hack
  printf("Corridor size %f!\n",
         map->get_block_map(grade)->line_of_sight(start.x,start.y,dest.x,dest.y));
  g1_solvemap = 0; //(OLI) Debug hack
}
//}}}

void g1_path_window_class::draw_to_bitmap()
//{{{
{
  map_changed=0;

  int bmw=map->width()*CELL_SIZE, bmh=map->height()*CELL_SIZE, x,y, i,j, 
    mw=map->width(), mh=map->height(), px, py, ry;

  if (bitmap && (bitmap->width() !=  bmw  ||  bitmap->height() != bmh))
  {
    delete bitmap;
    bitmap=0;
  }

  if (!bitmap)
  {
    i4_pixel_format fmt;
    fmt.default_format();
    bitmap=new i4_image32(bmw, bmh, i4_pal_man.register_pal(&fmt));   
  }

  i4_image32::iterator block_pixel=bitmap->create_iterator(0,0), pixel;

  g1_block_map_class *block_map = map->get_block_map(grade);
  for (ry=0; ryvertex(x,y).height;
      
      color = critical_color(x,y);
      
      if (solvemap->is_visited(x,y))
        color |= 0x004f00;
      if (solvemap->is_ok(x,y))
        color = 0x008f00;

      if (block_map->is_blocked(x,y, G1_NORTH | G1_SOUTH | G1_EAST | G1_WEST))
        color |= 0x800000;

      w32 half_color = (color & 0xfefefe)>>1;

      int blockN, blockW;

      blockN = (y==mh-1 ||
                block_map->is_blocked(x,y,G1_NORTH) ||
                block_map->is_blocked(x,y+1,G1_SOUTH));
      blockW = (x==0 ||
                block_map->is_blocked(x,y,G1_WEST) ||
                block_map->is_blocked(x-1,y,G1_EAST));
      pixel=block_pixel;
      for (py=0; pyiterator_store(pixel, (  (px == 0 & blockW)? 0xffffff 
                                         : (py == 0 & blockN)? 0xffffff
                                         : (px|py == 0)? half_color 
                                         : color ));

        pixel += bitmap->width() - CELL_SIZE;
      }

      block_pixel += CELL_SIZE;

    }
    block_pixel += (CELL_SIZE-1) * bitmap->width();
  }

  i4_draw_context_class tmp_context(0,0,bitmap->width()-1,bitmap->height()-1);

#if 0
  //{{{ Draw Section Boundaries
  test_block_map::CBounds::CBlockPoint *p,*q;
  w16 pi=map->bounds.begin(); 
  while (map->bounds.next_point(pi)) 
  {
    p = map->bounds.get_point(pi);
    for (int l=0; l<4; l++) 
    {
      if (p->edge[l]>pi) {
        q = map->bounds.get_point(p->edge[l]);
        bitmap->line(int(p->x*CELL_SIZE),int((mh-p->y)*CELL_SIZE-1), 
                     int(q->x*CELL_SIZE),int((mh-q->y)*CELL_SIZE-1), 
                     0x808080, tmp_context);
      }
      bitmap->put_pixel(int(p->x*CELL_SIZE),int((mh-p->y)*CELL_SIZE)-1, 0x0080ff, tmp_context);
    }
  }
  //}}}
#endif

  for (j=1; jcriticals; j++)
  {
    g1_critical_graph_class::critical_point_class *crit = &critical_graph->critical[j];
    for (i=0; iconnections; i++) 
    {
      if (crit->connection[i].size[grade])
      {
        w32 k=crit->connection[i].ref;
        i4_float x1,y1, x2,y2, x3,y3, x4,y4;
        x1 = crit->x*CELL_SIZE + CELL_SIZE/2;
        y1 = (mh*CELL_SIZE-1) - (crit->y*CELL_SIZE + CELL_SIZE/2);
        x4 = critical_graph->critical[k].x*CELL_SIZE + CELL_SIZE/2;
        y4 = (mh*CELL_SIZE-1) - (critical_graph->critical[k].y*CELL_SIZE + CELL_SIZE/2);
        
        x2 = (x4-x1)*0.3 + x1;
        y2 = (y4-y1)*0.3 + y1;
        x3 = (x4-x1)*0.7 + x1;
        y3 = (y4-y1)*0.7 + y1;
        
        bitmap->line((w16)x1,(w16)y1,(w16)x2,(w16)y2, 
                     0x000800*(crit->connection[i].size[grade]), tmp_context);
        bitmap->line((w16)x2,(w16)y2,(w16)x3,(w16)y3, 0x404020, tmp_context);
      }
    }
  }

  
  for (j=points-1; j>0; j--)    // points solved in backwards order
  {
    w32
      x1 = w32(point[j*2-2])*CELL_SIZE + CELL_SIZE/2,
      y1 = (mh*CELL_SIZE-1) - w32(point[j*2-1])*CELL_SIZE - CELL_SIZE/2,
      x2 = w32(point[j*2+0])*CELL_SIZE + CELL_SIZE/2,
      y2 = (mh*CELL_SIZE-1) - w32(point[j*2+1])*CELL_SIZE - CELL_SIZE/2;

    bitmap->line(x1,y1,x2,y2,0xffff00, tmp_context);
  }

}
//}}}

void g1_path_window_class::parent_draw(i4_draw_context_class &context)
//{{{
{
  int mw=map->width(), mh=map->height();

  if (map_changed)
    draw_to_bitmap();
    
  bitmap->put_image(local_image, 0,0, context);

  int x,y;
  x = start.x * CELL_SIZE + CELL_SIZE/2+1 - start_icon->width()/2;
  y = (mh-1-start.y) * CELL_SIZE + CELL_SIZE/2+1 - start_icon->height()/2;

  start_icon->put_image_trans(local_image, x, y, 0, context);

  x = dest.x * CELL_SIZE + CELL_SIZE/2+1 - dest_icon->width()/2;
  y = (mh-1-dest.y) * CELL_SIZE + CELL_SIZE/2+1 - dest_icon->height()/2;

  dest_icon->put_image_trans(local_image, x, y, 0, context);

  for (int i=0; icriticals; i++)
  {
    x = sw32(critical_graph->critical[i].x)*CELL_SIZE
      + CELL_SIZE/2+1 - crit_icon->width()/2;
    y = (mh-1-sw32(critical_graph->critical[i].y))*CELL_SIZE
      + CELL_SIZE/2+1 - crit_icon->height()/2;
      
    crit_icon->put_image_trans(local_image, x, y, 0, context);
  }
}
//}}}
  
void g1_path_window_class::receive_event(i4_event *ev)
{
    
  switch (ev->type()) 
  {
    case i4_event::MOUSE_MOVE: 
      //{{{
    {
      CAST_PTR(mev, i4_mouse_move_event_class, ev);
        
      last_x = mev->x; last_y = mev->y;
    } break;
    //}}}
    case i4_event::MOUSE_BUTTON_UP: 
      //{{{
    {
      CAST_PTR(mbev, i4_mouse_button_up_event_class, ev);
        
      int cell_x=last_x/CELL_SIZE,
        cell_y=map->height()-1-(last_y/CELL_SIZE);
        
      // determine type
        
      switch (mode)
      {
        case P1_SET_START :
          //{{{
        {
          if (mbev->left())
          {
            start.x=cell_x;
            start.y=cell_y;
          }
          else
          {
            dest.x=cell_x;
            dest.y=cell_y;
          }
          solve();
          changed();
        } break;
        //}}}
        case P1_SET_DESTINATION :
          //{{{
        {
          dest.x=cell_x;
          dest.y=cell_y;
          solve();
          changed();
        } break;
        //}}}
        case P1_SET_BLOCK:
          //{{{
        {
          int flags=G1_NORTH | G1_EAST | G1_WEST | G1_SOUTH;
              
          if (mbev->left())
            map->get_block_map(grade)->block(cell_x, cell_y, flags);
          else
            map->get_block_map(grade)->unblock(cell_x, cell_y, flags);
              
          maker->make_criticals(map, critical_graph);
          solve();
          changed();
        } break;
        //}}}
        case P1_SET_CRITICAL:
          //{{{
        {
          g1_editor_instance.add_undo(G1_MAP_CRITICAL_POINTS);
          if (map) map->mark_for_recalc(G1_RECALC_CRITICAL_DATA);

          if (mbev->left() && mbev->right())
            critical_graph->criticals=0;
          else if (mbev->left()) 
            critical_graph->add_critical_point(cell_x, cell_y);
          else if (mbev->right())
          {
            if (critical_graph->criticals>0)
              --critical_graph->criticals;
          }
            
          request_redraw();
        } break;
        //}}}
      }
        
    } break;
    //}}}
    case i4_event::USER_MESSAGE:
      //{{{
    {
      CAST_PTR(uev, i4_user_message_event_class, ev);
      switch (uev->sub_type) 
      {
        case P1_SET_BLOCK :
        case P1_LAY_OBJECT :
        case P1_SET_START :
        case P1_SET_DESTINATION :
        case P1_SET_CRITICAL :
        case P1_STOP : 
        case P1_STEP :
          mode=uev->sub_type;
          break;

        case P1_SET_GRADE1:
        case P1_SET_GRADE2:
        case P1_SET_GRADE3:
        case P1_SET_GRADE4:
          if (grade == uev->sub_type - P1_SET_GRADE1)
            tofrom = !tofrom;
          else
          {
            grade = uev->sub_type - P1_SET_GRADE1;
            solvemap->set_block_map(map->get_block_map(grade));
            solve();
          }
          changed();
          break;

        case P1_SET_SIZE1:
        case P1_SET_SIZE3:
          size = uev->sub_type - P1_SET_SIZE1 + 1;
          solve();
          changed();
          break;

        case P1_RELOAD_MAP:
          //{{{
        {
          g1_editor_instance.add_undo(G1_MAP_CRITICAL_DATA);
          critical_graph = map->get_critical_graph();
          maker->make_criticals(map, critical_graph);
          changed();
        } break;
        //}}}

        case P1_LOAD_HOTSPOTS:
          //{{{
        {
          int i,j,n;
          g1_map_cell_class *c=&map->cell(0,0);

          int g1_takeover_pad_type=g1_get_object_type("takeover_pad");

          g1_editor_instance.add_undo(G1_MAP_CRITICAL_POINTS);
          for (j=0; jheight(); j++)
            for (i=0; iheight(); i++, c++)
            {
              for (g1_object_chain_class *obj=c->get_obj_list(); obj; obj=obj->next)
              {
                if (obj->object->id == g1_takeover_pad_type)
                {
                  int found=0;
                  for (n=0; ncriticals; n++)
                    if (critical_graph->critical[n].x==i &&
                        critical_graph->critical[n].y==j)
                      found = 1;
                  if (!found)
                  {
                    critical_graph->critical[critical_graph->criticals].x = i;
                    critical_graph->critical[critical_graph->criticals].y = j;
                    critical_graph->criticals++;
                  }
                }
              }
            }
              
          changed();
        } break;
        //}}}
      }      
    }
    //}}}
  }
}

//{{{ Emacs Locals
// Local Variables:
// folded-file: t
// End:
//}}}