/**
 * Parse a given string/list properties and return the Map corresponding to the Map description
 *
 * @param 				{List} 				$properties 			The properties to parse
 * @param 				{Map} 				$descriptor 			The descriptor map to use to parse the properties
 * @return 				{Map} 										The properties in map format
 *
 * @example
 * s-parse-properties(10px hello -delay 20s, (
 *  	myNumber : Number,
 *  	myOtherNumber : Number,
 *  	myString : String,
 *  	myCoolVar : String,
 *  	delay : second
 * ));
 * // => (
 * //  	myNumber : 10px,
 * // 	myString : hello,
 * // 	delay : 20s
 * // )
 *
 * @author 			Olivier Bossel <olivier.bossel@gmail.com> (https://olivierbossel.com)
 */
@function s-parse-properties($properties, $keywords) {
  // check if is a map or a list
  @if type-of($properties) == map {
    $new-map: ();

    // loop on each properties to
    @each $prop, $value in $properties {
      // get the real name
      $prop: _s-get-property-name($prop, $keywords);

      // if we have a property
      @if $prop {
        // get the type
        $type: _s-get-property-type($prop, $keywords);

        // if we have found, check the type
        @if $type {
          // check the property type with the value
          @if _s-check-property($prop, $type, $value) {
            $new-map: map-set($new-map, $prop, $value);
          }
        }
      }
    }

    // return new map
    @return $new-map;
  } @else {
    $map: ();
    $values: ();
    $current_prop: null;
    $first: true;
    @each $prop in $properties {
      @if type-of($prop) == string and str-slice($prop, 1, 1) == "-" {
        // remove the - at property beginning
        $prop: str-slice($prop, 2);

        // get the real name
        $prop: _s-get-property-name($prop, $keywords);

        // we have a new property
        // need to check if we have already one in memory
        // to save it in the map after checking the type
        @if $current_prop {
          @if length($values) == 0 {
            $values: true;
          } @else if length($values) == 1 and type-of($values) == list {
            $values: nth($values, 1);
          }
          $type: _s-get-property-type($current_prop, $keywords);

          // check the property type with the value
          @if _s-check-property($prop, $type, $values) {
            $map: map-set($map, $current_prop, $values);
          }
        }
        $first: false;
        @if $prop {
          $current_prop: $prop;
          $values: ();
        }
      } @else {
        @if $first {
          // check if the type is ok
          $found: false;
          @each $name, $type in $keywords {
            @if $found == false {
              @if _s-check-property($name, $type, $prop, true) {
                $map: map-set($map, $name, $prop);
                $keywords: map-remove($keywords, $name);
                $found: true;
              }
            }
          }
        } @else {
          $values: append($values, $prop);
        }

        @if $current_prop {
          @if length($values) == 0 {
            $values: true;
          } @else if length($values) == 1 and type-of($values) == list {
            $values: nth($values, 1);
          }
          $type: _s-get-property-type($current_prop, $keywords);

          // check the property type with the value
          @if _s-check-property($current_prop, $type, $values) {
            $map: map-set($map, $current_prop, $values);
          }
        }
      }
    }
    @if $current_prop {
      @if length($values) == 0 {
        $values: true;
      } @else if length($values) == 1 and type-of($values) == list {
        $values: nth($values, 1);
      }
      $type: _s-get-property-type($current_prop, $keywords);

      // check the property type with the value
      @if _s-check-property($current_prop, $type, $values) {
        $map: map-set($map, $current_prop, $values);
      }
    }

    // return map
    @return $map;
  }
}

@function _s-check-property($property, $type, $value, $check-only: false) {
  $required: false;
  // check if is a map
  @if type-of($type) == map {
    $type: map-get($type, type);
    $required: map-get-or($type, required, $required);
  }

  // check required
  @if $required and not $value {
    @error ('The property #{$property} is required...');
  }

  // if not value, return false
  @if not $value {
    @return false;
  }

  // check explicit values
  @if type-of($type) == string {
    // check the type
    @if not s-is($value, $type) {
      @if $check-only {
        @return false;
      }
      @error ('The property "#{$property}" need to be a "#{$type}" but it seems that you pass a "#{type-of($value)}" : #{$value}...');
    }
    @return true;
  } @else if type-of($type) == list {
    // check if the passed value exist in possible values
    @if type-of($value) == list and index($type, length) {
      @return true;
    } @else if type-of($value) == number and index($type, length) {
      @return true;
    } @else {
      // if its a list of values, check each values
      @if type-of($value) == list {
        @each $v in $value {
          @if not _s-check-property($property, $type, $v, true) {
            @return false;
          }
        }
        @return true;
      } @else {
        $idx: index($type, $value);
        @if not $idx {
          @return false;
        }
        $v: nth($type, $idx);
        @if type-of($v) == type-of($value) {
          @if type-of($v) == number {
            @if unit($v) == unit($value) {
              @return true;
            } @else {
              @return false;
            }
          } @else {
            @return true;
          }
        } @else {
          @return false;
        }
        @return true;
      }
    }
  } @else {
    @return true;
  }
  @return false;
}

@function _s-get-property-name($property, $keywords: ()) {
  @if not $keywords {
    $keywords: ();
  }

  // check if exist
  $type: map-get($keywords, $property);
  // if we have a type, mean a property names like this
  // exist, return it
  @if $type {
    @return $property;
  }

  // nothing found
  @return null;
}

@function _s-get-property-type($property, $keywords: ()) {
  @if not $keywords {
    $keywords: ();
  }

  // check if exist
  $type: map-get($keywords, $property);

  // return the type
  @return $type;
}
