/*
 * Copyright (C) 2023 Robert Bosch GmbH Copyright (C) 2023 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { Duration, DurationType, MaxNumberOfDurations } from "./bci-duration-picker.model";

const DAYS_SEPARATOR = '.';
const HMS_SEPARATOR = ':';

/**
 * Parses the string type of duration into object type.
 */
export function parseDurationStringToObject(duration: string, durationType: DurationType): Duration | null {
  const durationArray: number[] = convertToArray(duration) as number[];
  const durationTypeArray: string[] = convertToArray(DurationType[durationType]) as string[];
  let durationObject: Duration;

  if(durationArray.length === durationTypeArray.length) {
    for(let i = 0; i< durationArray.length; i++) {
      switch (durationTypeArray[i]) {
        case 'ddd': {
          durationObject = {
            ...durationObject,
            days: Number(durationArray[i])
          }
          break;
        }
        case 'HH': {
          durationObject = {
            ...durationObject,
            hours: Number(durationArray[i])
          }
          break;
        }
        case 'MM': {
          durationObject = {
            ...durationObject,
            minutes: Number(durationArray[i])
          }
          break;
        }
        case 'SS': {
          durationObject = {
            ...durationObject,
            seconds: Number(durationArray[i])
          }
          break;
        }
      }
    }
  } else {
    return null;
  }

  return durationObject;
}

/**
 * Parses the object type of duration into string type.
 */
export function parseDurationObjectToString(duration: Duration): string | null {
  const days = duration.days >= 0 ? duration.days : '';
  const dot = duration.days >= 0 && duration.hours >= 0 ? DAYS_SEPARATOR : '';
  const hours = duration.hours >= 0 ? duration.hours : '';
  const hm = duration.hours >= 0 && duration.minutes >= 0 ? HMS_SEPARATOR : '';
  const minutes = duration.minutes >= 0 ? duration.minutes : '';
  const ms = duration.minutes >= 0 && duration.seconds >= 0 ? HMS_SEPARATOR : '';
  const seconds = duration.seconds >= 0 ? duration.seconds : '';
  const finalString = `${days}${dot}${hours}${hm}${minutes}${ms}${seconds}`

  return finalString && finalString !== '' ? finalString : null;
}

/**
 * Checks if the duration and durationTypes matches
 */
export function matchDurationWithDurationType(duration: Duration | string, durationType: DurationType): boolean {
  let durationArray: string[] | number[];
  const durationTypeArray: string[] | number[] = convertToArray(DurationType[durationType]);

  if(typeof duration === 'object') {
    durationArray = Object.keys(duration);
    const adaptedDurationArray = adaptDurationArray(durationArray);
    return adaptedDurationArray.join() === durationTypeArray.join();
  } else if(typeof duration === 'string') {
    durationArray = convertToArray(duration);
    return durationArray.length === durationTypeArray.length;
  }
}

/**
 * This function fill automatically (based on durationType) the missing values of duration
 * if the user enters just a single number and returns duration as Duration
 * Example: '125', will be '125.00:00' if the durationType is 'ddd.HH:MM'
 * @param firstValue - the value entered by the user
 * @param durationType - the duration type of the DurationPicker
 */
export function autoFillDuration(firstValue: number, durationType: DurationType): Duration {
  let duration: Duration;
  const typeArray: string[] = convertToArray(DurationType[durationType]) as string[];

  for ( const type of typeArray) {
    switch (type) {
      case 'ddd': {
        duration = {
          ...duration,
          days: typeArray[0] === 'ddd' ? Number(firstValue) : 0
        }
        break;
      }
      case 'HH': {
        duration = {
          ...duration,
          hours: typeArray[0] === 'HH' ? Number(firstValue) : 0
        }
        break;
      }
      case 'MM': {
        duration = {
          ...duration,
          minutes: typeArray[0] === 'MM' ? Number(firstValue) : 0
        }
        break;
      }
      case 'SS': {
        duration = {
          ...duration,
          seconds: typeArray[0] === 'SS' ? Number(firstValue) : 0
        }
        break;
      }
    }
  }

  return duration;
}

/**
 *This function fit the input duration to max number of duration accepted
 * Example:
 *  - duration: '123.99'
 *  - durationType: 'ddd.HH'
 *  - maxNbOfDurations: hours = 23
 *  => After using fitToMasValues function, the duration will be: '123.23'
 */
export function fitDurationToMaxValues(duration: string, maxNumberOfDurations: MaxNumberOfDurations, durationType: DurationType): string {
  const durationObj = parseDurationStringToObject(duration, durationType);
  let fitDuration: Duration;

  if(durationObj.days >= 0) {
    fitDuration = {
      ...fitDuration,
      days: durationObj.days <= maxNumberOfDurations.maxDays || !maxNumberOfDurations.maxDays ? durationObj.days : maxNumberOfDurations.maxDays
    }
  }

  if(durationObj.hours >= 0) {
    fitDuration = {
      ...fitDuration,
      hours: durationObj.hours <= maxNumberOfDurations.maxHours || !maxNumberOfDurations.maxHours ? durationObj.hours : maxNumberOfDurations.maxHours
    }
  }

  if(durationObj.minutes >= 0) {
    fitDuration = {
      ...fitDuration,
      minutes: durationObj.minutes <= maxNumberOfDurations.maxMinutes || !maxNumberOfDurations.maxMinutes ? durationObj.minutes : maxNumberOfDurations.maxMinutes
    }
  }

  if(durationObj.seconds >= 0) {
    fitDuration = {
      ...fitDuration,
      seconds: durationObj.seconds <= maxNumberOfDurations.maxSeconds || !maxNumberOfDurations.maxSeconds ? durationObj.seconds : maxNumberOfDurations.maxSeconds
    }
  }

  return parseDurationObjectToString(fitDuration);
}
/**
 * This function converts a string that respects a durationType pattern into an array
 * of specific durations (ddd, HH, MM, SS) or duration values (numbers)
 * Example:
 *  - 'ddd.HH:MM:SS' will be ['ddd', 'HH', 'MM', 'SS']
 *  - '333.22:11' will be ['333', '22', '11']
 */
function convertToArray(duration: string): number[] | string[] {
  let durationArray: number[] | string[] = [];
  let extractedHoursMinutesSeconds: number[] | string[] = [];
  let extractedDays: number[] | string[] = [];

  if(duration.includes(DAYS_SEPARATOR)) {
    extractedDays = duration.split(DAYS_SEPARATOR);
    extractedHoursMinutesSeconds = extractedDays[1].split(HMS_SEPARATOR);
    durationArray = [extractedDays[0], ...extractedHoursMinutesSeconds];
  } else {
    durationArray = duration.split(HMS_SEPARATOR);
  }

  return isDurationNumber(duration) ? durationArray.map((char: string) => parseInt(char, 10)) : durationArray;
}

/**
 * This function adapts the duration array to match the durationType
 * Example: ['days', 'hours', 'minutes'] will be 'ddd.HH:MM'
 */
function adaptDurationArray(durationArray: string[]): string[] {
  const adaptedArray: string[] = [];
  for(const element of durationArray) {
    switch (element) {
      case 'days': {
        adaptedArray.push('ddd');
        break;
      }
      case 'hours': {
        adaptedArray.push('HH');
        break;
      }
      case 'minutes': {
        adaptedArray.push('MM');
        break;
      }
      case 'seconds': {
        adaptedArray.push('SS');
        break;
      }
    }
  }
  return adaptedArray;
}

function isDurationNumber(value): boolean {
  const pattern = /^[0-9]{3}\.[0-9]{2}:[0-9]{2}:[0-9]{2}$/;
  return pattern.test(value);
}
