--******************************************************************************************
function public.Init( pos_z )

  interface.LoadImplementation( "itempanel" );

  private.is_objects_moving = false;
  private.count_visible_objects = ConfigGetInventorySize();
  private.m_left_object_index = 0; -- left visible object index
  private.m_objects = {};
  private.m_objects_params = {};
  private.m_dragging_object = nil;

  private.already_hide = nil;
  private.first_move = nil;
  private.ho_items = {};
  private.items_hub = "";

  ObjAttach( "int_itempanel", interface.originhub );
  ObjSet( "int_itempanel", { pos_z = pos_z } );
  private.event_anim_end = {};
  MsgSubscribe( Command_Interface_UpdateTextures, private.WideScreenUpdate );
  MsgSubscribe( Event_Level_CheatKeyPressed, private.HotKey );

end;
--******************************************************************************************
function public.Destroy()

  MsgUnsubscribe( Command_Interface_UpdateTextures, private.WideScreenUpdate );
  MsgUnsubscribe( Event_Level_CheatKeyPressed, private.HotKey );

end;
--******************************************************************************************
function public.EventAnimEnd( event_id )

  local event_params = private.event_anim_end[ event_id ];

------------------------------------------------------------------------------------
  if ( event_id == "show" ) then

  ObjSet( "int_itempanel", { input = true } );

------------------------------------------------------------------------------------
  elseif ( event_id == "hide" ) then

    ObjSet( "int_taskpanel", { active = false, visible = false } );

    local ho_items = private.ho_items;

    for i = 1, #ho_items, 1 do

      local item_name = ho_items[ i ][ "name" ];

      local def_name = interface.GetObjectName( item_name );
      local hub_name = "hub_int_itempanel_"..def_name;
      local sub_name = "sub_int_itempanel_"..def_name;
      local obj_name = "itm_int_itempanel_"..def_name;

      --DbgTrace( "IP hide: "..item_name.." -> "..private.items_hub );

      ObjAttach( item_name, private.items_hub );
      ObjDelete( hub_name );

    end;

------------------------------------------------------------------------------------
  elseif ( string.find( event_id, "item_move" ) ) then

    private.is_objects_moving = false;

------------------------------------------------------------------------------------
  elseif ( event_id == "item_drag" ) then

    local item_name = event_params[ "name" ];

    private.m_dragging_object = item_name;
    ObjSet( private.m_dragging_object, { input = true } );

------------------------------------------------------------------------------------
  elseif ( event_id == "item_drop" ) then

    local item_name = event_params[ "name" ];

    ObjSet( item_name, { input = true } );

------------------------------------------------------------------------------------
  elseif ( event_id == "item_menter" ) then

  --

------------------------------------------------------------------------------------
  elseif ( event_id == "item_mleave" ) then

  --

------------------------------------------------------------------------------------
  end;

end;
--******************************************************************************************
function public.Show( ho_items )

  if private.m_objects ~= nil then
    private.m_objects = nil;
  end;

  private.m_objects_params = {};
  private.m_objects = {};

  for i = 1, #ho_items do

    table.insert( private.m_objects, 
      { name = ho_items[ i ].name, hint = ho_items[ i ].hint } );

    local item_name = ho_items[ i ].name;
    local item_params = ObjGet( item_name );

    private.m_objects_params[ item_name ] = {};

    if item_params.event_startdrag == nil then
      item_params.event_startdrag = function () end;
    end;
    private.m_objects_params[ item_name ].event_startdrag = item_params.event_startdrag;

    if item_params.event_dragdrop == nil then
      item_params.event_dragdrop = function () end;
    end;
    private.m_objects_params[ item_name ].event_dragdrop = item_params.event_dragdrop;

    if item_params.event_menter == nil then
      item_params.event_menter = function () end;
    end;
    private.m_objects_params[ item_name ].event_menter = item_params.event_menter;

    if item_params.event_mleave == nil then
      item_params.event_mleave = function () end;
    end;
    private.m_objects_params[ item_name ].event_mleave = item_params.event_mleave;

    ObjSet( item_name, { 
      drag = true ,
      event_startdrag = function () private.StartDrag( item_name ); end,
      event_dragdrop  = function () private.StopDrag( item_name ); end,
      event_menter    = function () private.ItemMouseEnter( item_name ); end,
      event_mleave    = function () private.ItemMouseLeave( item_name ); end
    } );

  end;

  private.m_left_object_index = 0;

  local ho_name = interface.GetObjectName( GetCurrentRoom() );

  private.items_hub = "obj_"..ho_name.."_items";

  ObjAttach( private.items_hub, "interface_hub" );

  private.already_hide = false;
  private.first_move = true;

  ObjSet( "int_itempanel", { active = true, visible = true } );

  if ( not ho_items ) then

    ho_items = {};

  end;

  private.ho_items = ho_items;

  for i = 1, #ho_items, 1 do

    local item_name = ho_items[ i ][ "name" ];

    local def_name = interface.GetObjectName( item_name );
    local hub_name = "hub_int_itempanel_"..def_name;
    local sub_name = "sub_int_itempanel_"..def_name;
    local obj_name = "itm_int_itempanel_"..def_name;

    ObjCreate( hub_name, "obj" );
    ObjSet( hub_name,
    {
      pos_x = ( int_itempanel_impl.ITEM_POSITION_OFFSET * ( i - 1 ) ),
      pos_y = 0
    } );
    ObjAttach( hub_name, "obj_int_itempanel" );

    ObjCreate( sub_name, "obj" );
    ObjAttach( sub_name, hub_name );

    ObjCreate( obj_name, "obj" );
    ObjAttach( obj_name, sub_name );   

    ObjSet( item_name, { input = true, pos_x = 0, pos_y = 0 } );
    ObjAttach( item_name, obj_name );

    local item_params = ObjGet( item_name );

    ObjSet( obj_name,
    {
      pos_z = 13,

      scale_x = int_itempanel_impl.ITEM_SCALE,
      scale_y = int_itempanel_impl.ITEM_SCALE,

      inputrect_init = true,
      inputrect_x    = - ( 0.5 * item_params.draw_width  ),
      inputrect_y    = - ( 0.5 * item_params.draw_height ),
      inputrect_w    = item_params.draw_width,
      inputrect_h    = item_params.draw_height
    } );

    private.ItemCreate( item_name, sub_name );

  end;

  local event_id = "show";
  private.event_anim_end[ event_id ] = {};
  int_itempanel_impl.ShowAnim( event_id );

  private.MoveObjects();

end;
--******************************************************************************************
function public.Hide()

  if ( not private.already_hide ) then

    private.already_hide = true;

    for item_name, params in pairs( private.m_objects_params ) do
      ObjSet( item_name, params );
    end;

    private.m_objects_params = nil;

    private.m_objects = nil;
    private.m_left_object_index = 0;
    private.m_dragging_object = nil;
    private.is_objects_moving = false;

    ObjSet( "int_itempanel", { input = false } );

    local event_id = "hide";
    private.event_anim_end[ event_id ] = {};
    int_itempanel_impl.HideAnim( event_id );

  end;

end;
--******************************************************************************************
function public.StdItemFlyAnimation( anim_obj_name, target_obj_name, trigger_end, first_item )

  first_item = first_item or 0;

  SendMessage( Command_Transporter_MoveObject,
  {
    type       = Trans_SceneToInventory,
    obj_name   = anim_obj_name,
    toobj_name = target_obj_name,
    show       = first_item,
    end_trig   = trigger_end
  } );

end;
--******************************************************************************************
function public.StdItemDragAnimation( anim_obj_name, target_obj_name, trigger_end )

  SendMessage( Command_Transporter_MoveObject,
  {
    type       = Trans_InventoryToCursor,
    obj_name   = anim_obj_name,
    toobj_name = target_obj_name,
    end_trig   = trigger_end
  } );

end;
--******************************************************************************************
function public.StdItemDropAnimation( anim_obj_name, target_obj_name, trigger_end, scale_end )

  SendMessage( Command_Transporter_MoveObject,
  {
    type       = Trans_CursorToInventory,
    obj_name   = anim_obj_name,
    toobj_name = target_obj_name,
    scale      = scale_end,
    end_trig   = trigger_end
  } );

end;
--******************************************************************************************
function public.DeleteObject( item_name )

  local index = 0;
  for i = 1, #private.m_objects do
    if private.m_objects[ i ].name == item_name then
      index = i;
      break;
    end;
  end;

  if index ~= 0 then

    if item_name == private.m_dragging_object then
      MsgSend( Command_Transporter_StopAnim, { type = Trans_InventoryToCursor } );
      private.m_dragging_object = nil;
      SetCursor( CURSOR_DEFAULT );
    end;
    ObjDetach( item_name );

    if index - 1 < private.m_left_object_index then
      private.m_left_object_index = private.m_left_object_index - 1;

    elseif index - 1 >= private.m_left_object_index and 
           index - 1 < private.m_left_object_index + private.count_visible_objects then
      if private.m_left_object_index > 0 then
        private.m_left_object_index = private.m_left_object_index - 1;
      end;

    end;

    table.remove( private.m_objects, index );
    private.MoveObjects();

    local def_name = interface.GetObjectName( item_name );
    local hub_name = "hub_int_itempanel_"..def_name;
    local sub_name = "sub_int_itempanel_"..def_name;
    local obj_name = "itm_int_itempanel_"..def_name;

    if ( int_itempanel_impl.DONT_DELETE_ITEMS ) then

      ObjAttach( item_name, "int_itempanel_items_hub" );

    else

      ObjDelete( item_name );

    end;

    ObjDelete( obj_name );
    ObjDelete( hub_name );

  end;

end;
--******************************************************************************************
function public.ArrowClick( arrow )

  if ( arrow == "left" and private.m_left_object_index > 0 ) then

    private.m_left_object_index = private.m_left_object_index  - 1;
    private.MoveObjects();

  elseif ( arrow == "right" and private.m_left_object_index + private.count_visible_objects < #private.m_objects ) then

    private.m_left_object_index = private.m_left_object_index + 1;
    private.MoveObjects();

  end;

end;
--******************************************************************************************
function public.ShowHoHint()

  if private.m_objects == nil then return; end;

  local hints_panel_obj = {};
  local hints_scene_obj = {};
  local right_idx = private.count_visible_objects;
  if #private.m_objects - private.m_left_object_index < right_idx then
    right_idx = #private.m_objects - private.m_left_object_index;
  end;

  for i = private.m_left_object_index + 1, right_idx do
    table.insert( hints_panel_obj, private.m_objects[ i ].name );
    table.insert( hints_scene_obj, private.m_objects[ i ].hint );
  end;

  if #hints_panel_obj == 0 then return; end;
  
  local hint_index = math.random( 1, #hints_panel_obj );
  local hints_table = { hint = {} };

  local panelobj = hints_panel_obj[ hint_index ];
  local panelobj_pos = GetObjPosByObj( panelobj );
  table.insert( hints_table.hint, 
    { pos_x = panelobj_pos[ 1 ], pos_y = panelobj_pos[ 2 ], obj_name = panelobj } );
--  DbgTrace( panelobj.."  "..panelobj_pos[ 1 ].." "..panelobj_pos[ 2 ]  );

  local sceneobj = hints_scene_obj[ hint_index ];
  local sceneobj_pos = GetObjPosByObj( sceneobj );
  table.insert( hints_table.hint, 
    { pos_x = sceneobj_pos[ 1 ], pos_y = sceneobj_pos[ 2 ], obj_name = sceneobj } );
--  DbgTrace( sceneobj.."  "..sceneobj_pos[ 1 ].." "..sceneobj_pos[ 2 ]  );

  MsgSend( Command_Effect_ShowHoHint, hints_table );

end;
--******************************************************************************************
function public.TryCompleteHo()

  if #private.m_objects == 0 then
    MsgSend( Event_DialogHo_Show, {} );
  end;

end;
--******************************************************************************************
function private.MoveObjects()

  if #private.m_objects == 0 then
    return;
  end; 
  private.is_objects_moving = true;

  local size    = private.count_visible_objects;
  local objects = {};

  for i = 1, #private.m_objects do
    table.insert( objects, 
      { name = private.m_objects[ i ].name, 
        pos = private.ItemPanelGetObjPos( i - 1, private.m_left_object_index, size ) } );
  end; 

  --DbgTrace( "IP MOVE: size = "..size..", objects count = "..#objects );

  ObjSet( "obj_int_itempanel_btn_left",  { input = false } );
  ObjSet( "obj_int_itempanel_btn_right", { input = false } );

  if ( not int_itempanel_impl.ArrowSetVisible ) then

    ObjSet( "spr_int_itempanel_btn_left_na",  { visible = true } );
    ObjSet( "spr_int_itempanel_btn_right_na", { visible = true } );

  else

    int_itempanel_impl.ArrowSetVisible( "left",  false );
    int_itempanel_impl.ArrowSetVisible( "right", false );

  end;

  for i = 1, #objects, 1 do

    local item_name = objects[ i ][ "name" ];
    local item_pos  = objects[ i ][ "pos" ];

    local hub_name = "hub_int_itempanel_"..interface.GetObjectName( item_name );

    local item_pos_old = ( ObjGet( hub_name ).pos_x ) / int_itempanel_impl.ITEM_POSITION_OFFSET;

    local move_pos = int_itempanel_impl.ITEM_POSITION_OFFSET * item_pos;

    if ( item_pos == -1 ) then

      ObjSet( "obj_int_itempanel_btn_left", { input = true } );

      if ( not int_itempanel_impl.ArrowSetVisible ) then

        ObjSet( "spr_int_itempanel_btn_left_na", { visible = false } );

      else

        int_itempanel_impl.ArrowSetVisible( "left", true );

      end;

    end;

    if ( item_pos == size ) then

      ObjSet( "obj_int_itempanel_btn_right", { input = true } );

      if ( not int_itempanel_impl.ArrowSetVisible ) then

        ObjSet( "spr_int_itempanel_btn_right_na", { visible = false } );

      else

        int_itempanel_impl.ArrowSetVisible( "right", true );

      end;

    end;

    if ( private.first_move ) then

      local item_visible = 1;

      if ( item_pos == - 1 ) or ( item_pos == size ) then

      item_visible = 0;

      end;

      int_itempanel_impl.ItemSet( hub_name, move_pos, item_visible );

      --DbgTrace( "IP MOVE FIRST: object "..i..": "..item_name..", position = "..item_pos..", visible = "..item_visible.."." );

    else

      local move_type = 0;

      if     ( ( item_pos_old >  - 1 ) and ( item_pos ==  - 1 ) )
      or     ( ( item_pos_old < size ) and ( item_pos == size ) )
      then

        move_type = 1;
      
      elseif ( ( item_pos_old ==  - 1 ) and ( item_pos >  - 1 ) )
      or     ( ( item_pos_old == size ) and ( item_pos < size ) )
      then

        move_type = 2;

      end;

      local event_id = "item_move_"..item_name;
      private.event_anim_end[ event_id ] = {};
      int_itempanel_impl.ItemMoveAnim( hub_name, move_type, move_pos, event_id );

      --DbgTrace( "IP MOVE: object "..i..": "..item_name..", pos = "..item_pos.." ( old = "..item_pos_old.." ), move_type = "..move_type.."." );

    end;

  end;

  if ( private.first_move ) then

    private.first_move = false;
    private.MoveEnd();

  else

    ObjSet( "tmr_int_itempanel_items_move",
    {
      endtrig = private.MoveEnd,
      time    = int_itempanel_impl.ITEM_MOVE_ANIM_TIME,
      playing = true
    } );

  end;

end;
--******************************************************************************************
function private.FlyToCursor( item_name )

  SetCursor( CURSOR_NULL );

  local def_name = interface.GetObjectName( item_name );
  local hub_name = "hub_int_itempanel_"..def_name;
  local sub_name = "sub_int_itempanel_"..def_name;
  local obj_name = "itm_int_itempanel_"..def_name;

  --SendMessage( Event_ItemPanel_FlyAnimationStart );
  
  local event_id = "item_drag";
  private.event_anim_end[ event_id ] = { name = item_name };

  int_itempanel_impl.ItemDragAnim( obj_name, sub_name, event_id );

end;
--******************************************************************************************
function private.FlyToPanel( item_name )

  SetCursor( CURSOR_DEFAULT );

  local def_name = interface.GetObjectName( item_name );
  local hub_name = "hub_int_itempanel_"..def_name;
  local sub_name = "sub_int_itempanel_"..def_name;
  local obj_name = "itm_int_itempanel_"..def_name;

  --SendMessage( Event_ItemPanel_FlyAnimationStart );

  local event_id = "item_drop";
  private.event_anim_end[ event_id ] = { name = item_name };

  int_itempanel_impl.ItemDropAnim( obj_name, sub_name, event_id );

end;
--******************************************************************************************
function private.MoveEnd()

  private.is_objects_moving = false;

end;
--******************************************************************************************
function private.ZoomIn( item_name )

  local def_name = interface.GetObjectName( item_name );
  local hub_name = "hub_int_itempanel_"..def_name;
  local sub_name = "sub_int_itempanel_"..def_name;
  local obj_name = "itm_int_itempanel_"..def_name;

  local event_id = "item_menter";
  private.event_anim_end[ event_id ] = {};

  int_itempanel_impl.ItemMouseEnterAnim( obj_name, event_id );

end;
--******************************************************************************************
function private.ZoomOut( item_name )

  local def_name = interface.GetObjectName( item_name );
  local hub_name = "hub_int_itempanel_"..def_name;
  local sub_name = "sub_int_itempanel_"..def_name;
  local obj_name = "itm_int_itempanel_"..def_name;

  local event_id = "item_mleave";
  private.event_anim_end[ event_id ] = {};

  int_itempanel_impl.ItemMouseLeaveAnim( obj_name, event_id );

end;
--******************************************************************************************
function private.StartDrag( item_name )

  private.m_dragging_object = item_name;  
  ObjSet( item_name, { input = false } );
  private.FlyToCursor( item_name );
  SetCursor( CURSOR_NULL );

  if private.m_objects_params[ item_name ] and 
     private.m_objects_params[ item_name ].event_startdrag ~= nil then 
     private.m_objects_params[ item_name ].event_startdrag(); 
  end;

end;
--******************************************************************************************
function private.StopDrag( item_name )

  MsgSend( Command_Transporter_StopAnim, { type = Trans_InventoryToCursor } );
  ObjSet( item_name, { input = false } );

  if private.m_objects_params[ item_name ] and 
     private.m_objects_params[ item_name ].event_dragdrop ~= nil then 
     private.m_objects_params[ item_name ].event_dragdrop(); 
  end;

  if ObjGet( item_name ) ~= nil then
    private.FlyToPanel( item_name );
  end;
  private.m_dragging_object = nil; 
  SetCursor( CURSOR_DEFAULT );

end;
--******************************************************************************************
function private.ItemMouseEnter( item_name ) 

  private.ZoomIn( item_name ); 

  if private.m_objects_params[ item_name ] and 
     private.m_objects_params[ item_name ].event_menter ~= nil then 
     private.m_objects_params[ item_name ].event_menter(); 
  end;

end
--******************************************************************************************
function private.ItemMouseLeave( item_name ) 

  private.ZoomOut( item_name ); 

  if private.m_objects_params[ item_name ] and 
     private.m_objects_params[ item_name ].event_mleave ~= nil then 
     private.m_objects_params[ item_name ].event_mleave(); 
  end;

end
--******************************************************************************************
function private.ItemCreate( item_name, hub_name )

  local back_name = "spr_int_itempanel_"..interface.GetObjectName( item_name ).."_back";

  ObjCreate( back_name, "spr" );

  ObjSet( back_name,
  {
    res = "assets/interface/resources/item_back",
    input = false,
    blendmode = 2,
    pos_z = 0
  } );

  ObjAttach( back_name, hub_name );

end;
--******************************************************************************************
function private.HotKey( msg, params )

  if params.key == 13 --[[Enter - key]] then  

  elseif params.key == 32 --[[Space - key]] then 
    MsgSend( Event_DialogHo_Show, {} );

  end;

end;
--******************************************************************************************
function private.WideScreenUpdate()

  interface.WideScreenUpdate( "itempanel" );

end;
--******************************************************************************************
function private.ItemPanelGetObjPos( idx, start, m )

  if idx < start then
    return -1;
  elseif idx - start < m then
    return idx - start;
  else
    return m;
  end;

end;
--******************************************************************************************