import { getDomPos, getScrollTop, getScrollLeft } from './index'

// Calculate floated target position for tooltip and dropdown export default function getFloatedTargetPos(options) {

const { src, target, place, align, offset = 0, offsetLeft = 0, offsetTop = 0 } = options

const { x: x1, y: y1 } = getDomPos(src)

const w1 = src.offsetWidth
const h1 = src.offsetHeight

const w2 = target.offsetWidth
const h2 = target.offsetHeight

let pos = getPos({ x1, y1, w1, h1, w2, h2, place, align, offset })
let posWithOffsets = addExtraOffsets({ pos, offsetLeft, offsetTop })

// auto detect the best placement and alignment
const detectedPlace = detectPlace({ pos: posWithOffsets, place, w2, h2 })
const detectedAlign = detectAlign({ pos: posWithOffsets, place, align, w2, h2 })

const placeDifferent = (place !== detectedPlace)
const alignDifferent = (align !== detectedAlign)

if (placeDifferent || alignDifferent) {
  pos = getPos({
    x1, y1, w1, h1, w2, h2,
    place: detectedPlace, align: detectedAlign, offset
  })

  let adjustedOffsetLeft = offsetLeft
  let adjustedOffsetTop = offsetTop

  if (placeDifferent && ['left', 'right'].includes(detectedPlace)) {
    adjustedOffsetLeft = -adjustedOffsetLeft
  }
  if (placeDifferent && ['top', 'bottom'].includes(detectedPlace)) {
    adjustedOffsetTop = -adjustedOffsetTop
  }

  posWithOffsets = addExtraOffsets({
    pos,
    offsetLeft: adjustedOffsetLeft,
    offsetTop: adjustedOffsetTop
  })
}

const posWithSafeBoundary = adjustToBoundary({
  pos: posWithOffsets,
  w1, h1, w2, h2,
  place, align
})
return {
  pos: posWithSafeBoundary,
  place: detectedPlace,
  align: detectedAlign
}

}

function getTouchedData({ pos, w2, h2 }) {

const { left, top } = pos
const scrollTop = getScrollTop()
const scrollBottom = scrollTop + window.innerHeight
const scrollLeft = getScrollLeft()
const scrollRight = scrollLeft + window.innerWidth
const touchedTop = (top < scrollTop)
const touchedBottom = ((top + h2) > scrollBottom)
const touchedLeft = (left < scrollLeft)
const touchedRight = ((left + w2) > scrollRight)
return { touchedTop, touchedBottom, touchedLeft, touchedRight }

}

function detectPlace({ pos, place, w2, h2 }) {

const { touchedTop, touchedBottom,
  touchedLeft, touchedRight } = getTouchedData({ pos, w2, h2 })

if ((place === 'top') && touchedTop) {
  return 'bottom'
}
if ((place === 'bottom') && touchedBottom) {
  return 'top'
}
if ((place === 'left') && touchedLeft) {
  return 'right'
}
if ((place === 'right') && touchedRight) {
  return 'left'
}
return place

}

function detectAlign({ pos, place, align, w2, h2 }) {

const isVertical = ['top', 'bottom'].includes(place)
const isHorizontal = ['left', 'right'].includes(place)
const { touchedTop, touchedBottom,
  touchedLeft, touchedRight } = getTouchedData({ pos, w2, h2 })

if (isVertical && touchedLeft) {
  return 'left'
}
if (isVertical && touchedRight) {
  return 'right'
}
if (isHorizontal && touchedTop) {
  return 'top'
}
if (isHorizontal && touchedBottom) {
  return 'bottom'
}
return align

}

function getPos(options) {

const { place, align = 'center' } = options

switch (`${place}_${align}`) {
  case 'top_left':
    return getTopLeftPos(options)
  case 'top_right':
    return getTopRightPos(options)
  case 'top_center':
    return getTopCenterPos(options)
  case 'bottom_left':
    return getBottomLeftPos(options)
  case 'bottom_right':
    return getBottomRightPos(options)
  case 'bottom_center':
    return getBottomCenterPos(options)
  case 'left_top':
    return getLeftTopPos(options)
  case 'left_bottom':
    return getLeftBottomPos(options)
  case 'left_center':
    return getLeftCenterPos(options)
  case 'right_top':
    return getRightTopPos(options)
  case 'right_bottom':
    return getRightBottomPos(options)
  case 'right_center':
    return getRightCenterPos(options)
  default:
    throw new Error(`Unsupported placement and alignment: ${place} ${align}`)
}

}

function getTopLeftPos({ x1, y1, h2, offset }) {

const top = y1 - offset - h2
const left = x1
return { left, top }

}

function getTopRightPos({ x1, y1, w1, w2, h2, offset }) {

const top = y1 - offset - h2
const left = x1 - (w2 - w1)
return { left, top }

}

function getTopCenterPos({ x1, y1, w1, w2, h2, offset }) {

const top = y1 - offset - h2
const left = x1 + (w1 / 2) - (w2 / 2)
return { left, top }

}

function getBottomLeftPos({ x1, y1, h1, offset }) {

const top = y1 + h1 + offset
const left = x1
return { left, top }

}

function getBottomRightPos({ x1, y1, w1, h1, w2, offset }) {

const top = y1 + h1 + offset
const left = x1 - (w2 - w1)
return { left, top }

}

function getBottomCenterPos({ x1, y1, w1, h1, w2, offset }) {

const top = y1 + h1 + offset
const left = x1 + (w1 / 2) - (w2 / 2)
return { left, top }

}

function getLeftTopPos({ x1, y1, w2, offset }) {

const left = x1 - offset - w2
const top = y1
return { left, top }

}

function getLeftBottomPos({ x1, y1, h1, w2, h2, offset }) {

const left = x1 - offset - w2
const top = y1 - (h2 - h1)
return { left, top }

}

function getLeftCenterPos({ x1, y1, h1, w2, h2, offset }) {

const left = x1 - offset - w2
const top = y1 + (h1 / 2) - (h2 / 2)
return { left, top }

}

function getRightTopPos({ x1, y1, w1, offset }) {

const left = x1 + w1 + offset
const top = y1
return { left, top }

}

function getRightBottomPos({ x1, y1, w1, h1, h2, offset }) {

const left = x1 + w1 + offset
const top = y1 - (h2 - h1)
return { left, top }

}

function getRightCenterPos({ x1, y1, w1, h1, h2, offset }) {

const left = x1 + w1 + offset
const top = y1 + (h1 / 2) - (h2 / 2)
return { left, top }

}

function adjustToBoundary({ pos, w1, w2, h1, h2 }) {

const { touchedTop, touchedBottom,
  touchedLeft, touchedRight } = getTouchedData({ pos, w2, h2 })

let left = pos.left
let top = pos.top
if (touchedLeft) {
  left = 0 + getScrollLeft()
}
if (touchedRight) {
  left = window.innerWidth - w2 + w1;
}
if (touchedTop) {
  top = 0 + getScrollTop()
}
if (touchedBottom) {
  top = window.innerHeight - h2 + h1
}
return { top, left }

}

function addExtraOffsets({ pos, offsetLeft = 0, offsetTop = 0 }) {

return {
  left: pos.left + offsetLeft,
  top: pos.top + offsetTop
}

}