/*
 * Implementation of class MirrorPosition
 *
 * $Date$
 * $Revision*
 *
 */

#include "Wave/MirrorPosition.hh"
#include "WImage/miscdefs.h"
#include <cassert>
#ifndef _WIN32_WCE
#include <iostream>
#endif

#if !(defined __WATCOMC__ && __WATCOMC__ < 1230)
using namespace std;
#endif

MirrorPosition::MirrorPosition (bool laxis, bool raxis) 
{
  m_leftaxis  = laxis;
  m_rightaxis = raxis;
}

/*
 * On the mirroring:
 * 
 * We get a periodic signal with a period length of either:
 * + 2 * length for two axis
 * + 2 * (length - 1) for no axis
 * + 2 * length - 1 for one axis
 *
 * Depending on whether we've got a negative or positive index
 * it will be mirrored in the opposite direction. The first 
 * part of this movement is parallel to the absolute index
 * value, then it will go opposite, so for either positive or
 * negative positions we will always look at the first and
 * second half separately.
 * 
 * Note that the symmetric sign change can currently only 
 * occur with two axis settings (as the vectors by definition
 * are powers of two). If this is subject to change one day
 * the sign change intervals may have to be fixed!
 */

int
MirrorPosition::twoaxis (int position, int start, int length, int &sign)
{
  int period = (int)MUL2 (length);
      
  assert (position < 0 || position >= length);
  
  if (position < 0)
    {
      position = (-position) % period;

      if (position == 0)
        {
          return start;
        }
      else if (position <= length)
        {
          sign = m_symmetry? -1: 1;
          return start + position - 1;
        }
      else
        {
          return start - position + period;
        }
    }

  position = length + (position - length) % period;

  if (position < period)
    {
      sign = m_symmetry? -1: 1;
          return start + position - MUL2 (position - length) - 1;
    }

  return start + position - period;
}

int
MirrorPosition::noaxis (int position, int start, int length, int &sign)
{
  int period = (int)MUL2 (length - 1);
  
  assert (position < 0 || position >= length);

  if (position < 0)
    {
      position = (-position) % period;
      if (position < length)
        {
          sign = ((m_symmetry && position > 0)? -1: 1);
          return start + position;
        }
      return start - position + period;
    }
  else if (position >= length)
    {
      position = length + (position - length) % period;
      if (position < period)
        {
          sign = (m_symmetry? -1: 1);
          return start + position - MUL2 (position - length + 1);
        }
      sign = ((m_symmetry && position < period + 1)? -1: 1);
      return start + position - period;
    }

  /* not reached */
  return start;
}

int
MirrorPosition::oneleftaxis(int position, int start, int length, int &sign)
{
  sign = 1;
  int period = MUL2 (length) - 1;
  assert (position < 0 || position >= length);
  if (position < 0)
    {
      position = (-position) % period;
      if (position == 0)
        {
          return start;
        }
      if (position < length)
        {
          sign = (m_symmetry? -1: 1);
          return start - position + MUL2 (position) - 1;
        }
      return start - position + period;
    }

  position = length + (position - length) % period;

  if (position < period)
    {
      sign = (m_symmetry? -1: 1);
      return start + position - MUL2 (position - length + 1);
    }

  return start + position - period;
}

int
MirrorPosition::onerightaxis(int position, int start, int length, int &sign)
{
  sign = 1;
  int period = MUL2 (length) - 1;
  assert (position < 0 || position >= length);
  if (position < 0)
    {
      position = (-position) % period;
      if (position == 0)
        {
          return start;
        }
      if (position < length)
        {
          sign = (m_symmetry? -1: 1);
          return start - position + MUL2 (position);
        }
      return start - position + period;
    }

  position = length + (position - length) % period;
  if (position < period)
    {
      sign = (m_symmetry? -1: 1);
      return start + position - MUL2 (position - length) - 1;
    }

  return start + position - period;
}


int
MirrorPosition::pos (int position, int start, int length, int &sign)
{
  sign = 1;
  
  if (length <= 1)
    {
      return start;
    }
  else if (position >= 0 && position < length)
    {
      return start + position;
    }
  
  if (m_leftaxis && m_rightaxis)
    {
      return twoaxis (position, start, length, sign);
    }
  else if (!(m_leftaxis || m_rightaxis))
    {
      return noaxis (position, start, length, sign);
    }
  else if (!m_leftaxis && m_rightaxis)
    {
      return onerightaxis (position, start, length, sign);
    }

  return oneleftaxis (position, start, length, sign);
}

