#include "models/items.h"

void PickItem(ecs_iter_t *it) {
    Position *p = ecs_field(it, Position, 2);
    Inventory *inv = ecs_field(it, Inventory, 3);
    
    for (int i = 0; i < it->count; i++) {
        if (inv[i].pickup_time > game_time()) continue;
        size_t ents_count;
        int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
        bool picked = false;
        
        for (size_t j = 0; j < ents_count; j++) {
            Item *drop = 0;
            uint64_t ent_id = ents[j];
            if ((drop = ecs_get_mut_if(it->world, ent_id, Item))) {
                Position *p2 = ecs_get_mut(it->world, ent_id, Position);
                Velocity *v2 = ecs_get_mut(it->world, ent_id, Velocity);
                
                float dx = p2->x - p[i].x;
                float dy = p2->y - p[i].y;
                float range = zpl_sqrt(dx*dx + dy*dy);
                if (range <= game_rules.item_pick_radius) {
                    uint16_t drop_id = item_find(drop->kind);
                    for (size_t k = 0; k < ITEMS_INVENTORY_SIZE; k += 1) {
                        ecs_entity_t item_slot_ent = inv[i].items[k];
                        Item *item = item_get_data(item_slot_ent);
                        uint16_t item_id = item ? item_find(item->kind) : 0;
                        if (!item || (item_id != ASSET_INVALID && (item->kind == drop->kind && item->durability == drop->durability) && item->quantity < item_max_quantity(drop_id))) {
                            if (item) {
                                uint32_t picked_count = zpl_max(0, (int32_t)drop->quantity);
                                picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
                                item->quantity += picked_count;
                                drop->quantity -= picked_count;
                                item->kind = drop->kind;
                                
                                if (drop->quantity == 0)
                                    item_despawn(ent_id);
                            } else if (!world_entity_valid(item_slot_ent)) {
                                entity_wake(ent_id);
                                item_show(ent_id, false);
                                inv[i].items[k] = ent_id;
                            }
                            picked = true;
                            break;
                        }
                    }
                } else if (range <= game_rules.item_attract_radius) {
                    v2->x = (p[i].x - p2->x) * game_rules.item_attract_force;
                    v2->y = (p[i].y - p2->y) * game_rules.item_attract_force;
                }
            }
            
            if (picked) break;
        }
    }
}

void CraftItem(ecs_iter_t *it) {
    Input *in = ecs_field(it, Input, 1);
    
    for (int i = 0; i < it->count; i++) {
        if (in[i].craft_item == 0) continue;
        if (world_entity_valid(in[i].storage_ent)){
            Producer *ic = 0;
            if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, Producer))){
                ic->target_item = in[i].craft_item;
                if (ic->pending_task == PRODUCER_CRAFT_WAITING) {
                    ic->pending_task = PRODUCER_CRAFT_ENQUEUED;
                }
                in[i].craft_item = 0;
            }
        }
    }
}

void DropItem(ecs_iter_t *it) {
    Input *in = ecs_field(it, Input, 1);
    Position *p = ecs_field(it, Position, 2);
    Inventory *inv = ecs_field(it, Inventory, 3);
    
    for (int i = 0; i < it->count; i++) {
        if (!in[i].drop) continue;
        
        ecs_entity_t *items = inv[i].items;
        
        if (in[i].storage_action){
            if (world_entity_valid(in[i].storage_ent)){
                ItemContainer *ic = 0;
                if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
                    items = ic->items;
                }else{
                    continue;
                }
            }else{
                continue;
            }
        }
        
        uint8_t slot_id = in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item;
        ecs_entity_t item_slot_ent = items[slot_id];
        Item *item = item_get_data(item_slot_ent);
        
        if (!item || item->quantity <= 0)
            continue;
        
        uint32_t dropped_count = item->quantity;
        if (in[i].sprint) {
            dropped_count /= 2;
        } else if (in[i].ctrl) {
            dropped_count = item->quantity-1;
        }
        
        if (dropped_count == 0)
            continue;
        
        item_show(item_slot_ent, true);
        
        Position *ipos = ecs_get_mut(it->world, item_slot_ent, Position);
        entity_set_position(item_slot_ent, p[i].x, p[i].y);
        
        Velocity *v = ecs_get_mut(it->world, item_slot_ent, Velocity);
        v->x = in[i].mx * 800.0f;
        v->y = in[i].my * 800.0f;
        
        inv[i].pickup_time = game_time() + game_rules.item_drop_pickup_time;
        in[i].drop = false;
        items[slot_id] = 0;
        
        if (item->quantity - dropped_count > 0) {
            item->quantity -= dropped_count;
            ecs_entity_t te = item_spawn(item->kind, dropped_count);
            item_show(te, false);
            items[slot_id] = te;
        }
    }
}

// void MergeItems(ecs_iter_t *it) {
//     Position *p = ecs_field(it, Position, 1);
//     ItemDrop *id = ecs_field(it, ItemDrop, 2);

//     for (int i = 0; i < it->count; i += 1) {
//         ItemDrop *item = &id[i];

//         if (item->merger_time < game_time())
//             continue;

//         size_t ents_count;
//         int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 1);

//         for (size_t j = 0; j < ents_count; j++) {
//             ItemDrop *drop = 0;
//             if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
//                 if (drop->kind != item->kind || (ecs_entity_t)ents[j] == it->entities[i] || drop->quantity == 0 || item->quantity == 0)
//                     continue;

//                 Position const* p2 = ecs_get(it->world, ents[j], Position);

//                 float dx = p2->x - (p[i].x);
//                 float dy = p2->y - (p[i].y);
//                 float range = zpl_sqrt(dx*dx + dy*dy);
//                 if (range <= game_rules.item_merger_radius) {
//                     drop->quantity += item->quantity;
//                     item_despawn(it->entities[i]);
//                     break;
//                 }
//             }
//         }
//     }
// }

void SwapItems(ecs_iter_t *it) {
    Input *in = ecs_field(it, Input, 1);
    Inventory *inv = ecs_field(it, Inventory, 2);
    
    for (int i = 0; i < it->count; i++) {
        if (!in[i].swap) continue;
        
        ecs_entity_t *items = inv[i].items;
        
        if (in[i].storage_action){
            if (world_entity_valid(in[i].storage_ent)){
                ItemContainer *ic = 0;
                if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
                    items = ic->items;
                }else{
                    continue;
                }
            }else{
                continue;
            }
        }
        
        ecs_entity_t *from_ent = 0;
        ecs_entity_t *to_ent = 0;
        Item *to = 0;
        Item *from = 0;
        
        if (in[i].swap_storage){
            in[i].swap_storage = false;
            
            if (in[i].storage_action){
                from_ent = &inv[i].items[in[i].swap_from];
                to_ent = &items[in[i].swap_to];
                from = item_get_data(*from_ent);
                to = item_get_data(*to_ent);
            }else{
                if (world_entity_valid(in[i].storage_ent)){
                    ItemContainer *ic = 0;
                    if ((ic = ecs_get_mut_if(it->world, in[i].storage_ent, ItemContainer))){
                        from_ent = &ic->items[in[i].swap_from];
                        from = item_get_data(*from_ent);
                    }else{
                        continue;
                    }
                }else{
                    continue;
                }
                to_ent = &items[in[i].swap_to];
                to = item_get_data(*to_ent);
            }
        }else{
            from_ent = &items[in[i].swap_from];
            to_ent = &items[in[i].swap_to];
            from = item_get_data(*from_ent);
            to = item_get_data(*to_ent);
        }
        
        if (!from) continue;
        
        uint16_t to_id = to ? item_find(to->kind) : ASSET_EMPTY;
        asset_id to_kind = to ? to->kind : ASSET_EMPTY;
        
        if (to_ent == from_ent) {
            // NOTE(zaklaus): do nothing
        } else if (to_kind == from->kind && to->quantity > 0) {
            uint32_t swapped_count = from->quantity;
            if (in[i].sprint) {
                swapped_count /= 2;
            } else if (in[i].ctrl) {
                swapped_count = 1;
            }
            swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
            to->quantity += swapped_count;
            from->quantity -= swapped_count;
            
            if (from->quantity == 0) {
                item_despawn(*from_ent);
                *from_ent = 0;
            }
        } else if ((in[i].ctrl || in[i].sprint) && to == 0 && from->quantity > 0) {
            // NOTE(zaklaus): item split
            uint32_t split_count = from->quantity / 2;
            if (in[i].ctrl) {
                split_count = 1;
            }
            if (from->quantity - split_count == 0) {
                continue;
            }
            ecs_entity_t te = item_spawn(from->kind, split_count);
            item_show(te, false);
            *to_ent = te;
            
            from->quantity -= split_count;
        } else {
            ecs_entity_t tmp = *to_ent;
            *to_ent = *from_ent;
            *from_ent = tmp;
        }
        
        in[i].swap = false;
    }
}

void UseItem(ecs_iter_t *it) {
    Input *in = ecs_field(it, Input, 1);
    Position *p = ecs_field(it, Position, 2);
    Inventory *inv = ecs_field(it, Inventory, 3);
    
    for (int i = 0; i < it->count; i++) {
        if (!in[i].use && !in[i].num_placements) continue;
        
        if (in[i].storage_action){
            continue;
        }
        
        ecs_entity_t item_ent = 0;
        Item *item = NULL;
        uint16_t item_id = 0;
        item_usage usage = UKIND_DELETE;
        
        if (!in[i].deletion_mode){
            item_ent = inv[i].items[in[i].selected_item];
            item = item_get_data(item_ent);
            item_id = item ? item_find(item->kind) : ASSET_EMPTY;
            usage = item_get_usage(item_id);
            if (!item || item->quantity <= 0) continue;
        }
        
        if (!in[i].use && usage == UKIND_DELETE){
            for (size_t j = 0; j < in[i].num_placements; j++) {
                world_chunk_destroy_block(in[i].placements_x[j], in[i].placements_y[j], true);
            }
        }
        else if (in[i].use && usage > UKIND_END_PLACE)
            item_use(it->world, item_ent, item, p[i], 0);
        else if (in[i].num_placements > 0 && usage < UKIND_END_PLACE) {
            asset_id ofs = 0;
            if (item_get_place_directional(item_id) && in[i].num_placements >= 2) {
                float p1x = in[i].placements_x[0];
                float p1y = in[i].placements_y[0];
                float p2x = in[i].placements_x[1];
                float p2y = in[i].placements_y[1];
                float sx = zpl_sign0(p2x-p1x);
                float sy = zpl_sign0(p2y-p1y);
                ofs = (sx < 0.0f) ? 1 : 2;
                if (sx == 0.0f) {
                    ofs = (sy < 0.0f) ? 3 : 4;
                }
            } else if(item_get_place_directional(item_id)) {
                // NOTE(zaklaus): ensure we pick the first variant
                ofs = 1;
            }
            
            for (size_t j = 0; j < in[i].num_placements; j++) {
                Position pos = {.x = in[i].placements_x[j], .y = in[i].placements_y[j]};
                item_use(it->world, item_ent, item, pos, ofs);
            }
            
            in[i].num_placements = 0;
        }
        
        entity_wake(it->entities[i]);
        
        if (usage != UKIND_DELETE && item->quantity == 0) {
            item_despawn(item_ent);
            inv[i].items[in[i].selected_item] = 0;
        }
    }
}

void InspectContainers(ecs_iter_t *it) {
    Input *in = ecs_field(it, Input, 1);
    
    for (int i = 0; i < it->count; ++i) {
        if (!in[i].pick) continue;
        
        if ((in[i].sel_ent && ecs_get(it->world, in[i].sel_ent, ItemContainer)) || !in[i].sel_ent)
            in[i].storage_ent = in[i].sel_ent;
    }
}

void HarvestIntoContainers(ecs_iter_t *it) {
    ItemContainer *in = ecs_field(it, ItemContainer, 1);
    Position *p = ecs_field(it, Position, 2);
    
    for (int i = 0; i < it->count; ++i) {
        // NOTE(zaklaus): find any item
        size_t ents_count;
        int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 0);
        bool picked = false;
        
        for (size_t j = 0; j < ents_count; j++) {
            Item *drop = 0;
            if ((drop = ecs_get_mut_if(it->world, ents[j], Item))) {
                Position *p2 = ecs_get_mut(it->world, ents[j], Position);
                uint64_t ent_id = ents[j];
                
                float dx = p2->x - p[i].x;
                float dy = p2->y - p[i].y;
                float range = zpl_sqrt(dx*dx + dy*dy);
                if (range <= game_rules.item_pick_radius) {
                    uint16_t drop_id = item_find(drop->kind);
                    for (size_t k = 0; k < ITEMS_CONTAINER_SIZE; k += 1) {
                        uint64_t item_slot_ent = in[i].items[k];
                        Item *item = item_get_data(item_slot_ent);
                        uint16_t item_id = item ? item_find(item->kind) : 0;
                        if (!item || (item_id != ASSET_INVALID && (item->kind == drop->kind && item->durability == drop->durability) && item->quantity < item_max_quantity(drop_id))) {
                            if (item) {
								uint32_t picked_count = zpl_max(0, (int32_t)drop->quantity);
                                picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
                                item->quantity += picked_count;
                                drop->quantity -= picked_count;
                                
                                if (drop->quantity == 0) {
                                    item_despawn(ent_id);
                                }
                            } else if (!world_entity_valid(item_slot_ent)) {
                                entity_wake(ent_id);
                                item_show(ent_id, false);
                                in[i].items[k] = ent_id;
                            }
                            entity_wake(it->entities[i]);
                            picked = true;
                            break;
                        }
                    }
                }
            }
            if (picked) break;
        }
    }
}

void ThrowItemsOut(ecs_iter_t *it) {
    ItemContainer *storage = ecs_field(it, ItemContainer, 1);
    Position *p = ecs_field(it, Position, 2);
    
    for (int i = 0; i < it->count; i++) {
        for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
            ecs_entity_t item_slot_ent = storage[i].items[j];
            Item *item = item_get_data(item_slot_ent);
            if (!item) continue;
            
            item_show(item_slot_ent, true);
            entity_set_position(item_slot_ent, p[i].x, p[i].y);
        }
    }
}
