/* Copyright Statement:
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to MediaTek Inc. and/or its licensors. Without
 * the prior written permission of MediaTek inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of MediaTek Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * Copyright  (C) [2020]  MediaTek Inc. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
 * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
 * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
 * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
 *
 * The following software/firmware and/or related documentation ("MediaTek
 * Software") have been modified by MediaTek Inc. All revisions are subject to
 * any receiver's applicable license agreements with MediaTek Inc.
 */
#ifndef _ECMAP_H_
#define _ECMAP_H_

#include <linux/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct entry_ec {
    char * key;
    void * value;
    struct entry_ec * next;
} Entry;

typedef int boolean;
#define TRUE 1
#define FALSE 0

typedef struct hashMap_ec {
    int size;           //the number of element in set
    int capacity;
    int nodeLen;
    Entry **list;
    int dilatationCount;
    int dilatationSum;

} HashMap;

extern HashMap * EC_MAP;

typedef struct hashMapIterator_ec {
    Entry *nextEntry;
    int count;
    HashMap *hashMap;
    int index;
}HashMapIterator;

static unsigned int BKDRHash(char *str)
{
    unsigned int seed = 131;
    unsigned int hash = 0;

    while (*str)
    {
        hash = hash * seed + (*str++);
    }

    return (hash & 0x7FFFFFFF);
}

static  unsigned int defaultHashCode(HashMap hashMap, char * key){
    return BKDRHash(key)% hashMap.capacity;
}

HashMap *createHashMap(int capacity) {

    HashMap *hashMap= (HashMap *)malloc(sizeof(HashMap));

    if(capacity<10){
        capacity=10;
    }
    hashMap->size=0;
    hashMap->dilatationCount=0;
    hashMap->dilatationSum=0;
    hashMap->nodeLen=0;
    hashMap->capacity=capacity;
    hashMap->list = (Entry **)calloc(capacity,sizeof(Entry));
    return hashMap;
}

static int  expansionBase( HashMap *hashMap){
    int len = hashMap->capacity;
    int dilatationCount= hashMap->dilatationCount;
    hashMap->dilatationSum++;

    len+= (len>=100000000?len*0.2:
          len>=50000000?len*0.3:
          len>=10000000?len*0.4:
          len>=5000000?len*0.5:
          len>=1000000?len*0.6:
          len>=500000?len*0.7:
          len>=100000?len*0.8:
          len>=50000?len*0.9:
          len*1.0);
    hashMap->dilatationCount++;

    if(dilatationCount>=3){
        len+= (len>=100000000?len*1:
              len>=50000000?len*2:
              len>=10000000?len*3:
              len>=5000000?len*4:
              len>=1000000?len*5:
              len>=500000?len*6:
              len>=100000?len*7:
              len>=50000?len*8:
              len>=10000?len*9:
              len>=1000?len*10:
              len*20);
        hashMap->dilatationCount=0;
    }

    return len;
}

static  void dilatationHash(HashMap *hashMap){

    int capacity = hashMap->capacity;

    hashMap->capacity=expansionBase(hashMap);

    hashMap->nodeLen=0;

    Entry **newList=(Entry **)calloc(hashMap->capacity,sizeof(Entry));

    for(int i=0;i<capacity;i++){
        Entry *entry=hashMap->list[i];
        if(entry!=NULL){
            unsigned int newIndex=defaultHashCode(*hashMap,entry->key);
            if(newList[newIndex]==NULL){
                Entry *newEntry = (Entry *)malloc(sizeof(Entry));
                newEntry->key = entry->key;
                newEntry->value = entry->value;
                newEntry->next = NULL;
                newList[newIndex] = newEntry;
                hashMap->nodeLen++;
            }else{
                Entry *newEntry = (Entry *)malloc(sizeof(Entry));
                newEntry->key = entry->key;
                newEntry->value = entry->value;
                //insert the data to the head of List
                newEntry->next = newList[newIndex];
                newList[newIndex] = newEntry;
            }

            if(entry->next!=NULL){

                Entry *nextEntry=entry->next;
                while(nextEntry!=NULL){

                    unsigned int newIndex=defaultHashCode(*hashMap,nextEntry->key);
                    if(newList[newIndex]==NULL){
                        Entry *newEntry = (Entry *)malloc(sizeof(Entry));
                        newEntry->key = nextEntry->key;
                        newEntry->value = nextEntry->value;
                        newEntry->next = NULL;
                        newList[newIndex] = newEntry;
                        hashMap->nodeLen++;
                    }else{
                        Entry *newEntry = (Entry *)malloc(sizeof(Entry));
                        newEntry->key = nextEntry->key;
                        newEntry->value = nextEntry->value;

                        newEntry->next = newList[newIndex];
                        newList[newIndex] = newEntry;
                    }
                    nextEntry=nextEntry->next;
                }
            }
        }
    }

    free(hashMap->list);

    hashMap->list=newList;
}

int InsertHashMap(HashMap *hashMap, char *key, void *value) {

    if(hashMap->nodeLen == hashMap->capacity){
        dilatationHash(hashMap);
    }

    unsigned int hashCode = defaultHashCode(*hashMap, key);

    Entry *entry = hashMap->list[hashCode];

    if(entry == NULL){
		UBOOT_ERROR("InsertHashMap 0 | Single Key %s in Entry\n", key);
        Entry *newEntry = (Entry *)malloc(sizeof(Entry));
        newEntry->key = key;
        newEntry->value = value;
        newEntry->next = NULL;
        hashMap->list[hashCode] = newEntry;
        hashMap->size++;
        hashMap->nodeLen++;
        return CMD_RET_SUCCESS;
    }

    //judge duplicated key
    if(entry->key !=NULL && strcmp(entry->key, key)==0){
        UBOOT_ERROR("InsertHashMap 1 | Duplicated Key in Entry %s  and %s\n", entry->key, key);
        return -2;
    }


    while (entry != NULL) {
        if (strcmp(entry->key, key) == 0) {
            UBOOT_ERROR("InsertHashMap 2 | Duplicated Key in Entry %s  and %s\n", entry->key, key);
            return -2;
        }
        entry = entry->next;
    }
 
    Entry *newEntry = (Entry *)malloc(sizeof(Entry));
    newEntry->key = key;
    newEntry->value = value;

    newEntry->next = hashMap->list[hashCode];
    hashMap->list[hashCode] = newEntry;
    hashMap->size++;

    return CMD_RET_SUCCESS;
}

void *findHashMap(HashMap *hashMap, char *key) {

    unsigned int hashCode = defaultHashCode(*hashMap, key);

    Entry *entry = hashMap->list[hashCode];

    if(entry==NULL){
		UBOOT_ERROR("findHashMap | \"%s\" not exist\n", key);
        return NULL;
    }

    if(entry->key !=NULL && strcmp(entry->key,key)==0){
        return entry->value;
    }

    while (entry != NULL) {

        if (strcmp(entry->key, key) == 0) {
            return entry->value;
        }
        entry = entry->next;
    }

    UBOOT_ERROR("findHashMap | \"%s\" not exist\n", key);

    return NULL;
}

HashMapIterator *createHashMapIterator(HashMap *hashMap){
    HashMapIterator *hashMapIterator= malloc(sizeof(HashMapIterator));;
    hashMapIterator->hashMap = hashMap;
    hashMapIterator->count= 0;
    hashMapIterator->index= 0;
    hashMapIterator->nextEntry= NULL;

    return hashMapIterator;
}
boolean hasNextHashMapIterator(HashMapIterator *iterator){
    return iterator->count < iterator->hashMap->size ? TRUE : FALSE;
}
Entry *nextHashMapIterator(HashMapIterator *iterator) {
    if (hasNextHashMapIterator(iterator)) {

        if(iterator->nextEntry!=NULL){
            Entry *entry = iterator->nextEntry;
            iterator->nextEntry = entry->next;
            iterator->count++;
            return entry;
        }

        Entry *pEntry1 = iterator->hashMap->list[iterator->index];

        while (pEntry1==NULL){
            pEntry1 = iterator->hashMap->list[++iterator->index];
        }

        if(pEntry1->next==NULL){
            Entry *pEntry2= iterator->hashMap->list[++iterator->index];
            while (pEntry2==NULL){
                pEntry2 = iterator->hashMap->list[++iterator->index];
            }
            iterator->nextEntry =pEntry2;
        }else{
            iterator->nextEntry = pEntry1->next;
        }
        iterator->count++;
        return pEntry1;
    }
    return  NULL;
}
#endif
