#ifndef STD_LIB_H
#define STD_LIB_H
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include<math.h>
#include<time.h>
#include<ctype.h>
#include <stddef.h>
#endif
#ifndef _WIN32
//for fflush_stdin
#include <stdio_ext.h>
#endif
#define SIZE_OF_PAGE 20
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
void Cls(){
    #ifdef _WIN32
        system("cls");
    #else
        system("clear");
    #endif
}
void fflush_stdin(){
  #ifdef _WIN32
    fflush(stdin);
  #else
    __fpurge(stdin);
  #endif
}

char * welcome_message(void * ptr){ //cross compatible

    if(!ptr){//if fp is NULL
      return "><Welcome to the Student Union POS system><\n";
    }
    if(((FILE*)ptr)!=stdout){// if ptr a string or stdout
      sprintf(ptr,"><Welcome to the Student Union POS system><\n");//welcome message
    }else{
      fprintf(ptr,"><Welcome to the Student Union POS system><\n");//welcome message
    }
    return (char*)NULL;
}
int choices_selecter(char * Items[],int items,char * welcome_messages){
  int choice = 0;
  do{
  	Cls();

    if (welcome_messages !=NULL){
    	printf("%s",welcome_messages);
	}
	printf("Select An Option:\n");
    int i;
    for (i=0; i<items;i++){
      printf("%d. %s\n",i+1,Items[i]);
    }
    printf("%d. Exit\n",i+1);
    printf(">");
  	fflush_stdin();
    scanf("%d",&choice);
    if(choice < 1 || choice > items+1){
        printf("Invalid choice...press any key to retry....\n");
        fflush_stdin();
        getchar();
    }
  }while(choice < 1 || choice > items+1);
  return choice;

}

char* prompt_item(char* prompt,bool empty_allowed){
    char* item = malloc(sizeof(char) * 100);
    do{
        printf("%s",prompt);
        fflush_stdin();
        scanf("%[^\n]",item);//input until /n
        if(!empty_allowed&&strcmp(item,"") == 0){//check if temp is not empty
            printf("Invalid input, try again?(y/n)\n");
            char temp[100];
            fflush_stdin();
            scanf("%[^\n]",temp);
            if(strcmp(temp,"n") == 0){
                return NULL;
            }
        }
    }while(!empty_allowed&&strcmp(item,"") == 0);
    return item;
}
typedef enum{
  INT=1,STRING=2,LONG=3,DOUBLE=4
}INPUT_TYPE;
bool item_inputer(char * varname,INPUT_TYPE type,void * item,bool empty_allowed){
  char output[1024] = "Please input the ";
  strcat(output,varname);
  strcat(output,": \n>");
  char* temp = prompt_item(output,empty_allowed);
  if(temp == NULL){
        return false;
  }
  switch(type){
    case INT:
      *((int*)item) = atoi(temp);
      break;
    case STRING:
      strcpy(item,temp);
      break;
    case LONG:
      *((long*)item) = atol(temp);
      break;
    case DOUBLE:
      *((double*)item) = atof(temp);
      break;
    default:
      return false;
  }
  return true;
}

void lister(
  void * db,
  struct Map* map,
   int row,
   char * lister_name,
   char * SortItems[],
   int NoSortItems,
   void (*page_printer)(void*,int,int,struct Map*),
   struct Map* (*sorter)(void *,int),void * (*showitem)(void *,int))
{
  int choice = -1;
    int page = 0;
    int page_size = SIZE_OF_PAGE;
    int total_pages = ceil((double)row / page_size);
    do{
    Cls();
    welcome_message(stdout);
    printf("%s list\n",lister_name);
    printf("0 exit\n");
    for(int i=0;i<NoSortItems*2;i+=2){
      printf("%d sort %s decending\n",i+1,SortItems[i/2]);
      printf("%d sort %s ascending\n",i+2,SortItems[i/2]);
    }
    if(page+1 == total_pages){
        (*page_printer)(db,page*page_size,row,map);
    }else{
        (*page_printer)(db,page*page_size,(page+1)*page_size,map);
    }
    //page control
    int current_page_size = page_size+NoSortItems*2;
    if(page+1 == total_pages){
        current_page_size = row - page*page_size+NoSortItems*2;
    }
    printf("%d next page\n", current_page_size+1);
    printf("%d previous page\n", current_page_size+2);
    printf("%d set page size\n", current_page_size+3);
    printf("%d/%d/%d(page size/page number/total)\n",page_size, page+1,total_pages);

    bool valid = true;
    do{
        valid = true;
        printf("Please input your choice\n>");
        fflush_stdin();
        scanf("%d",&choice);
         if(choice <=NoSortItems*2 && choice > 0){
            printf("sorting...\n");
            map = (*sorter)(db,choice);
        }else if(choice == current_page_size+1 ){
            if(page + 1 < total_pages){
                page++;
            }else{
                printf("Already at last page\n");
                valid = false;
            }
        }else if(choice == current_page_size+2){
            if(page > 0){
                page--;
            }else{
                printf("Already at first page\n");
                valid = false;
            }
        }else if(choice == current_page_size+3){
            printf("Enter page size: ");
            fflush_stdin();
            scanf("%d", &page_size);
            total_pages = ceil((double)row / page_size);
        }else if(choice > NoSortItems*2 && choice <= current_page_size){
            if(map == NULL){
                db = (*showitem)(db,choice - NoSortItems*2-1 + page_size*page);
            }else{
                db = (*showitem)(db,map[choice - NoSortItems*2-1 + page_size*page].key);
            }   
        }else if(choice != 0){
            printf("Invalid choice\n");
            valid = false;
        }

    }while(!valid);
    }while(choice != 0);
    return;
}
//Size to be compressed to, > 3(...), should be some larger number else seeing nothing
char * StringCompression(char * str, int size){
  if(strlen(str) > size){
    str[size-3] = '.';
    str[size-2] = '.';
    str[size-1] = '.';
    str[size] = '\0';
  }
  return str;
}



//check if strcasestr is define in current environment
//if not, define it
#ifndef HAVE_STRCASESTR
char *strcasestr(const char *s, const char *find)
{
char c, sc;
size_t len;

if ((c = *find++) != 0) {
c = tolower((unsigned char)c);
len = strlen(find);
do {
do {
if ((sc = *s++) == 0)
return (NULL);
} while ((char)tolower((unsigned char)sc) != c);
} while (strncasecmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}
#endif

