$border-defaults: (
  'side': 'bottom',
  'size': 1px,
  'length': 1px,
  'gap': 1px,
  'color': #000,
  'start': 'origin',
  'translate': false
) !default;

@function border-construtor($parameters) {
  @if map-get($parameters, 'side') == 'vertical' {
    @return border-construtor( map-merge($parameters, ('side': top)) ),
            border-construtor( map-merge($parameters, ('side': bottom)) );
  }
  @if map-get($parameters, 'side') == 'horizontal' {
    @return border-construtor( map-merge($parameters, ('side': right)) ),
            border-construtor( map-merge($parameters, ('side': left)) );
  }
  @if map-get($parameters, 'side') == 'all' {
    @return border-construtor( map-merge($parameters, ('side': top)) ),
            border-construtor( map-merge($parameters, ('side': right)) ),
            border-construtor( map-merge($parameters, ('side': bottom)) ),
            border-construtor( map-merge($parameters, ('side': left)) );
  }
  
  @each $key, $value in $parameters {
    @if $value == _ {
      $parameters: map-merge($parameters, ($key: map-get($border-defaults, $key)));
    }
  }

  $side: param-get($parameters, 'side');
  $size: param-get($parameters, 'size');
  $length: param-get($parameters, 'length');
  $gap: param-get($parameters, 'gap');
  $color: param-get($parameters, 'color');
  $translate: param-get($parameters, 'translate');
  $start: param-get($parameters, 'start');
  
  $section: $length + $gap;
  @return (
    'bg-image': (linear-gradient(direction-builder($side, $start), gradient-builder($length, $section, $color))),
    'bg-size': (size-builder($side, $size, $section)),
    'bg-position': (position-builder($side, $start, $section, $translate)),
    'bg-repeat': (repeat-builder($side))
  );
}

@function param-get($parameters, $key) {
  $value: map-get($parameters, $key);
  @if $key == 'side' and $value != 'top' and $value != 'right' and $value != 'bottom' and $value != 'left' {
    @error 'Value #{$value} of property #{$key} must be either top, or right, or bottom, or left, or vertical, or horizontal, or all.';
  }
  @if ($key == 'size' or $key == 'length' or $key == 'gap') and (type-of($value) != number or type-of($value) == number and $value < 0) {
    @error 'Value #{$value} of property #{$key} must be non-negative size number.';
  }
  @if $key == 'color' and type-of($value) != color {
    @error 'Value #{$value} of property #{$key} must be color.';
  }
  @if $key == 'start' and $value != 'origin' and $value != 'center' and $value != 'opposite' {
    @error 'Value #{$value} of property #{$key} must be either origin, center, or opposite.';
  }
  @return $value;
}

@function gradient-builder($length, $section, $color) {
  @return $color, $color ($length / $section * 100%), transparent ($length / $section * 100%);
}

@function direction-builder($side, $start) {
  @if $start == 'origin' or $start == 'center' {
    @if $side == 'bottom' {
      @return to right;
    } @else if $side == 'top' {
      @return to left;
    } @else if $side == 'right' {
      @return to top;
    } @else if $side == 'left' {
      @return to bottom;
    } @else {
      @error "Property #{$side} is invalid.";
    }
  } @else if $start == 'opposite' {
    @if $side == 'bottom' {
      @return to left;
    } @else if $side == 'top' {
      @return to right;
    } @else if $side == 'right' {
      @return to bottom;
    } @else if $side == 'left' {
      @return to top;
    } @else {
      @error "Property #{$side} is invalid.";
    }
  } @else {
    @error "Property #{$start} is invalid.";
  }
}

@function position-builder($side, $start, $move, $translate) {
  @if not $translate {
    $move: 0.000001px;
  } @else if type-of($translate) == 'number' {
    $move: $move * $translate / ($translate * 0 + 1);
    @if round($translate) != $translate {
      @warn 'If you make an animation and use translate property with not an integer value, be sure that both of your values have the same decimal value to prevent unexpected intermittent behavior.';
    }
  }
  @if $start == 'center' {
    @warn "Property translate doesn't work when property start is center.";
    @return $side;
  } @else if $start == 'origin' {
    @if $side == 'bottom' {
      @return #{$move} 100%;
    } @else if $side == 'top' {
      @return calc(100% - #{$move}) 0;
    } @else if $side == 'right' {
      @return 100% calc(100% - #{$move});
    } @else if $side == 'left' {
      @return 0 #{$move};
    } @else {
      @error "Property #{$side} is invalid.";
    }
  } @else if $start == 'opposite' {
    @if $side == 'bottom' {
      @return calc(100% + #{$move}) 100%;
    } @else if $side == 'top' {
      @return calc(#{$move} * -1) 0;
    } @else if $side == 'right' {
      @return 100% calc(#{$move} * -1);
    } @else if $side == 'left' {
      @return 0 calc(100% + #{$move});
    } @else {
      @error "Property #{$side} is invalid.";
    }
  } @else {
    @error "Property #{$start} is invalid.";
  }
}

@function repeat-builder($side) {
  @if $side == 'top' or $side == 'bottom' {
    @return repeat-x;
  } @else if $side == 'right' or $side == 'left' {
    @return repeat-y;
  } @else {
    @error "Property #{$side} is invalid.";
  }
}

@function size-builder($side, $size, $section) {
  @if $side == 'top' or $side == 'bottom' {
    @return $section $size;
  } @else if $side == 'right' or $side == 'left' {
    @return $size $section;
  } @else {
    @error "Property #{$side} is invalid.";
  }
}

@function dotted($side: _, $size: _, $gap: _, $color: _, $translate: _, $start: _) {
  @return border-construtor(
    (
      'side': $side,
      'size': $size,
      'length': $size,
      'gap': $gap,
      'color': $color,
      'translate': $translate,
      'start': $start
    )
  );
}
@function dashed($side: _, $size: _, $length: _, $gap: _, $color: _, $translate: _, $start: _) {
  @return border-construtor(
    (
      'side': $side,
      'size': $size,
      'length': $length,
      'gap': $gap,
      'color': $color,
      'translate': $translate,
      'start': $start
    )
  );
}

@mixin custom-border($borders...) {
  $bg-image-list: ();
  $bg-position-list: ();
  $bg-size-list: ();
  $bg-repeat-list: ();

  @each $border-list in $borders {
    @if type-of($border-list) == list {
      @each $border in $border-list {
        $bg-image-list: append($bg-image-list, map-get($border, 'bg-image'), 'comma');
        $bg-size-list: append($bg-size-list, map-get($border, 'bg-size'), 'comma');
        $bg-position-list: append($bg-position-list, map-get($border, 'bg-position'), 'comma');
        $bg-repeat-list: append($bg-repeat-list, map-get($border, 'bg-repeat'), 'comma');
      }
    } @else {
      $border: $border-list;
      $bg-image-list: append($bg-image-list, map-get($border, 'bg-image'), 'comma');
      $bg-size-list: append($bg-size-list, map-get($border, 'bg-size'), 'comma');
      $bg-position-list: append($bg-position-list, map-get($border, 'bg-position'), 'comma');
      $bg-repeat-list: append($bg-repeat-list, map-get($border, 'bg-repeat'), 'comma');
    }
  }

  background-image: $bg-image-list;
  background-size: $bg-size-list;
  background-position: $bg-position-list;
  background-repeat: $bg-repeat-list;
}