text: /*
   * Copyright (c) 2012 cocos2d-x.org
   * http://www.cocos2d-x.org
   *
   * Copyright 2012 Yannick Loriot. All rights reserved.
   * http://yannickloriot.com
   * 
   * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
   * 
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   * 
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * 
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT falseT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND falseNINFRINGEMENT. IN false EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   *
   */
  
  #include "CCControlStepper.h"
  
  NS_CC_EXT_BEGIN
  
  #define ControlStepperLabelColorEnabled   Color3B(55, 55, 55)
  #define ControlStepperLabelColorDisabled  Color3B(147, 147, 147)
  
  #define ControlStepperLabelFont           "CourierNewPSMT"
  
  
  #define kAutorepeatDeltaTime                0.15f
  #define kAutorepeatIncreaseTimeIncrement    12
  
  ControlStepper::ControlStepper()
  : _value(0.0)
  , _continuous(false)
  , _autorepeat(false)
  , _wraps(false)
  , _minimumValue(0.0)
  , _maximumValue(0.0)
  , _stepValue(0.0)
  , _touchInsideFlag(false)
  , _touchedPart(Part::NONE)
  , _autorepeatCount(0)
  , _minusSprite(nullptr)
  , _plusSprite(nullptr)
  , _minusLabel(nullptr)
  , _plusLabel(nullptr)
  {
  
  }
  
  ControlStepper::~ControlStepper()
  {
      unscheduleAllCallbacks();
      
      CC_SAFE_RELEASE(_minusSprite);
      CC_SAFE_RELEASE(_plusSprite);
      CC_SAFE_RELEASE(_minusLabel);
      CC_SAFE_RELEASE(_plusLabel);
  }
  
  bool ControlStepper::initWithMinusSpriteAndPlusSprite(Sprite *minusSprite, Sprite *plusSprite)
  {
      if (Control::init())
      {
          CCASSERT(minusSprite,   "Minus sprite must be not nil");
          CCASSERT(plusSprite,    "Plus sprite must be not nil");
          
          // Set the default values
          _autorepeat                         = true;
          _continuous                         = true;
          _minimumValue                       = 0;
          _maximumValue                       = 100;
          _value                              = 0;
          _stepValue                          = 1;
          _wraps                              = false;
          this->ignoreAnchorPointForPosition( false );
      
          // Add the minus components
          this->setMinusSprite(minusSprite);
  		_minusSprite->setPosition(minusSprite->getContentSize().width / 2, minusSprite->getContentSize().height / 2);
  		this->addChild(_minusSprite);
          
          this->setMinusLabel( Label::createWithSystemFont("-", ControlStepperLabelFont, 40));
          _minusLabel->setColor(ControlStepperLabelColorDisabled);
          _minusLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
          _minusLabel->setPosition(_minusSprite->getContentSize().width / 2, _minusSprite->getContentSize().height / 2);
          _minusSprite->addChild(_minusLabel);
          
          // Add the plus components 
          this->setPlusSprite( plusSprite );
  		_plusSprite->setPosition(minusSprite->getContentSize().width + plusSprite->getContentSize().width / 2,
                                                    minusSprite->getContentSize().height / 2);
  		this->addChild(_plusSprite);
          
          this->setPlusLabel( Label::createWithSystemFont("+", ControlStepperLabelFont, 40 ));
          _plusLabel->setColor( ControlStepperLabelColorEnabled );
          _plusLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
          _plusLabel->setPosition(_plusSprite->getContentSize().width / 2, _plusSprite->getContentSize().height / 2);
          _plusSprite->addChild(_plusLabel);
          
          // Defines the content size
          Rect maxRect = ControlUtils::RectUnion(_minusSprite->getBoundingBox(), _plusSprite->getBoundingBox());
          this->setContentSize( Size(_minusSprite->getContentSize().width + _plusSprite->getContentSize().height, maxRect.size.height) );
          return true;
      }
      return false;
  }
  
  ControlStepper* ControlStepper::create(Sprite *minusSprite, Sprite *plusSprite)
  {
      ControlStepper* pRet = new (std::nothrow) ControlStepper();
      if (pRet != nullptr && pRet->initWithMinusSpriteAndPlusSprite(minusSprite, plusSprite))
      {
          pRet->autorelease();
      }
      else
      {
          CC_SAFE_DELETE(pRet);
      }
      return pRet;
  }
  
  //// Properties
  
  void ControlStepper::setWraps(bool wraps)
  {
      _wraps = wraps;
      
      if (_wraps)
      {
          _minusLabel->setColor( ControlStepperLabelColorEnabled );
          _plusLabel->setColor(ControlStepperLabelColorEnabled );
      }
      
      this->setValue( _value );
  }
  
  void ControlStepper::setMinimumValue(double minimumValue)
  {
      if (minimumValue >= _maximumValue)
      {
          CCASSERT(0, "Must be numerically less than maximumValue.");
      }
      
      _minimumValue   = minimumValue;
      this->setValue( _value );
  }
  
  void ControlStepper::setMaximumValue(double maximumValue)
  {
      if (maximumValue <= _minimumValue)
      {
          CCASSERT(0, "Must be numerically greater than minimumValue.");
      }
      
      _maximumValue   = maximumValue;
      this->setValue(_value);
  }
  
  void ControlStepper::setValue(double value)
  {
      this->setValueWithSendingEvent(value, true);
  }
  
  double ControlStepper::getValue() const
  {
      return _value;
  }
  
  void ControlStepper::setStepValue(double stepValue)
  {
      if (stepValue <= 0)
      {
          CCASSERT(0,"Must be numerically greater than 0.");
      }
  
      _stepValue  = stepValue;
  }
  
  bool ControlStepper::isContinuous() const
  {
      return _continuous;
  }
  //
  //// ControlStepper Public Methods
  
  void ControlStepper::setValueWithSendingEvent(double value, bool send)
  {
      if (value < _minimumValue)
      {
          value = _wraps ? _maximumValue : _minimumValue;
      } else if (value > _maximumValue)
      {
          value = _wraps ? _minimumValue : _maximumValue;
      }
      
      _value = value;
      
      if (!_wraps)
      {
          _minusLabel->setColor((value == _minimumValue) ? ControlStepperLabelColorDisabled : ControlStepperLabelColorEnabled);
          _plusLabel->setColor((value == _maximumValue) ? ControlStepperLabelColorDisabled : ControlStepperLabelColorEnabled);
      }
      
      if (send)
      {
          this->sendActionsForControlEvents(Control::EventType::VALUE_CHANGED);
      }
  }
  
  void ControlStepper::startAutorepeat()
  {
      _autorepeatCount    = -1;
      
      this->schedule(CC_SCHEDULE_SELECTOR(ControlStepper::update), kAutorepeatDeltaTime, CC_REPEAT_FOREVER, kAutorepeatDeltaTime * 3);
  }
  
  /** Stop the autorepeat. */
  void ControlStepper::stopAutorepeat()
  {
      this->unschedule(CC_SCHEDULE_SELECTOR(ControlStepper::update));
  }
  
  void ControlStepper::update(float /*dt*/)
  {
      _autorepeatCount++;
      
      if ((_autorepeatCount < kAutorepeatIncreaseTimeIncrement) && (_autorepeatCount % 3) != 0)
          return;
      
      if (_touchedPart == Part::MINUS)
      {
          this->setValueWithSendingEvent(_value - _stepValue, _continuous);
      } else if (_touchedPart == Part::PLUS)
      {
          this->setValueWithSendingEvent(_value + _stepValue, _continuous);
      }
  }
  
  //// ControlStepper Private Methods
  
  void ControlStepper::updateLayoutUsingTouchLocation(Vec2 location)
  {
      if (location.x < _minusSprite->getContentSize().width
          && _value > _minimumValue)
      {
          _touchedPart        = Part::MINUS;
          
          _minusSprite->setColor(Color3B::GRAY);
          _plusSprite->setColor(Color3B::WHITE);
      } else if (location.x >= _minusSprite->getContentSize().width
                 && _value < _maximumValue)
      {
          _touchedPart        = Part::PLUS;
          
          _minusSprite->setColor(Color3B::WHITE);
          _plusSprite->setColor(Color3B::GRAY);
      } else
      {
          _touchedPart        = Part::NONE;
          
          _minusSprite->setColor(Color3B::WHITE);
          _plusSprite->setColor(Color3B::WHITE);
      }
  }
  
  
  bool ControlStepper::onTouchBegan(Touch *pTouch, Event* /*pEvent*/)
  {
      if (!isTouchInside(pTouch) || !isEnabled() || !isVisible())
      {
          return false;
      }
      
      Vec2 location    = this->getTouchLocation(pTouch);
      this->updateLayoutUsingTouchLocation(location);
      
      _touchInsideFlag = true;
      
      if (_autorepeat)
      {
          this->startAutorepeat();
      }
      
      return true;
  }
  
  void ControlStepper::onTouchMoved(Touch *pTouch, Event* /*pEvent*/)
  {
      if (this->isTouchInside(pTouch))
      {
          Vec2 location    = this->getTouchLocation(pTouch);
          this->updateLayoutUsingTouchLocation(location);
          
          if (!_touchInsideFlag)
          {
              _touchInsideFlag    = true;
              
              if (_autorepeat)
              {
                  this->startAutorepeat();
              }
          }
      }
      else
      {
          _touchInsideFlag    = false;
          
          _touchedPart        = Part::NONE;
          
          _minusSprite->setColor(Color3B::WHITE);
          _plusSprite->setColor(Color3B::WHITE);
          
          if (_autorepeat)
          {
              this->stopAutorepeat();
          }
      }
  }
  
  void ControlStepper::onTouchEnded(Touch *pTouch, Event* /*pEvent*/)
  {
      _minusSprite->setColor(Color3B::WHITE);
      _plusSprite->setColor(Color3B::WHITE);
      
      if (_autorepeat)
      {
          this->stopAutorepeat();
      }
      
      if (this->isTouchInside(pTouch))
      {
          Vec2 location    = this->getTouchLocation(pTouch);
          
          this->setValue(_value + ((location.x < _minusSprite->getContentSize().width) ? (0.0-_stepValue) : _stepValue));
      }
  }
  
  NS_CC_EXT_END
  