// all Valid* functions return ErrorMsg - if everything is cool, the return value is TRUE
// all Check* functions focus the corresponding input and show alert with the error message

// If MaxLen<0 then string is nullable
function ValidString(Str, DisplayLabel, MinLen, MaxLen)
{
  Str = ''+Str;
  if(MinLen>0 && Str=='')
    return 'Specify '+DisplayLabel+', please.';
  else if(MaxLen>0 && Str.length>MaxLen)
    return DisplayLabel+' should not be greater than '+MaxLen+' characters long ('+
        Str.length+' now).';
  else if(Str.length<MinLen)
    return DisplayLabel+' should be at least '+MinLen+' characters long ('+
        Str.length+' now).';
  return true;
}

function CheckString(InputID, DisplayLabel, MinLen, MaxLen)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidString(Input.val(), DisplayLabel, MinLen, MaxLen);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function CheckSelect(InputID, DisplayLabel)
{
  var Input = $('#'+InputID);
  if(Input.val()==0)
  {
    Input.focus();
    alert('Please select a value for '+DisplayLabel+' from the dropdown list.');
    return false;
  }
  return true;
}

function ValidEmail(Email, DisplayLabel)
{
  var Result;
  if(!DisplayLabel)
    DisplayLabel = 'E-mail';
  Result = ValidString(Email, DisplayLabel, 1, 255);
  if(Result==true)
    if(!/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(Email))
      Result = 'Invalid '+DisplayLabel+': '+Email+'.';
  return Result;
}

function CheckEmail(InputID, DisplayLabel)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidEmail(Input.val(), DisplayLabel);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

// Valid examples:
// - john@smith.com
// - John Smith <john@smith.com>
// - "John Smith" <john@smith.com>
// - "John Smith" <john@smith.com>, <mary@smith.com>
// - "John Smith" <john@smith.com>,<mary@smith.com>
// - "John Smith" <john@smith.com>;mary@smith.com
// - "John Smith" <john@smith.com>; "Mary Smith" <mary@smith.com>
function ValidListOfEmails(ListOfEmails)
{
  var Emails = ListOfEmails.split(/[,|;]/);
  var i, a, n, Email, l, ErrorMsg, FirstName, LastName, j, QuoteChar;
  n = Emails.length;
  for(i = n-1; i>=0; i--)
    if(Emails[i]=='')
      Emails.splice(i, 1);
  var EmailCount = Emails.length;
  if(EmailCount==0)
    return 'Please specify a list of e-mails (comma-separated)';
  for(i = 0; i<EmailCount; i++)
  {
    s = Emails[i].trim();
    if(!s)
      continue;
    a = s.split(' ');
    n = a.length;
    Email = a[n-1];
    l = Email.length;
    if(l>2 && Email[0]=='<' && Email[l-1]=='>')
      Email = Email.substr(1, l-2);
    ErrorMsg = ValidEmail(Email);
    if(ErrorMsg!=true)
      return ErrorMsg;
    if(n>1)
    {
      FirstName = a[0];
      LastName = '';
      for(j = 1; j<n-1; j++)
      {
        if(LastName>'')
          LastName += ' ';
        LastName += a[j];
      }
      ErrorMsg = ValidFirstAndLastNameForEmail(FirstName, LastName);
      if(ErrorMsg!=true)
        return ErrorMsg;
    }
  }
  return true;
}

function ValidFirstAndLastNameForEmail(FirstName, LastName)
{
  var ErrorMsg;
  ErrorMsg = ValidFirstAndLastNameForEmail_ForQuoteChar(FirstName, LastName, '"');
  if(ErrorMsg!=true)
    return ErrorMsg;
  ErrorMsg = ValidFirstAndLastNameForEmail_ForQuoteChar(FirstName, LastName, "'");
  if(ErrorMsg!=true)
    return ErrorMsg;
  return true;
}

function ValidFirstAndLastNameForEmail_ForQuoteChar(FirstName, LastName, QuoteChar)
{
  if(FirstName[0]==QuoteChar)
    if(LastName>'')
    {
      l = LastName.length;
      if(LastName[l-1]!=QuoteChar)
        return 'Unclosed quote in e-mail list.';
      FirstName = FirstName.substr(1);
      LastName = LastName.substr(0, l-1);
    }
    else
    {
      l = FirstName.length;
      if(FirstName[l-1]!=QuoteChar)
        return 'Unclosed quote in e-mail list.';
      FirstName = FirstName.substr(1, l-2);
    }
  if(FirstName.indexOf(QuoteChar)>-1 || LastName.indexOf(QuoteChar)>-1)
    return 'You cannot use quote (") and apostrophe (\') characters in the e-mail addresses.';
  return true;
}

function CheckListOfEmails(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidListOfEmails(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidDateString(DateString)
{
  DateString = ''+DateString;
  if(DateString=='')
    return 'Please specify date';
  var ErrorMsg = 'Invalid date ('+DateString+'). Please use DD.MM.YYYY format.';
  var Day = '';
  var Month = null;
  var Year = null;
  var c;
  DateString = DateString.replace(',', '.');
  for(var i = 0; i<DateString.length; i++)
  {
    if(DateString.substr(i, 1)=='/' || DateString.substr(i, 1)=='.')
      if(Year || !i)
        return ErrorMsg;
      else if(DateString.substr(i-1, 1)=='/' || DateString.substr(i-1, 1)=='.')
        return ErrorMsg;
      else if(Month==null)
        Month = '';
      else
        Year = '';
    else
    {
      c = DateString.substr(i, 1);
      if(!IsDigit(c))
        return ErrorMsg;
      else
        if(Month==null)
          Day += c;
        else if(Year==null)
          Month += c;
        else
          Year += c;
    }
  }
  Day = parseInt(Day, 10);
  Month = parseInt(Month, 10);
  Year = parseInt(Year, 10);
  if(Year==null || parseInt(Year, 10)>2999)
    return ErrorMsg;
  if(Year<=70)
    Year += 2000;
  else if(Year<100)
    Year += 1900;
  if(Month>=1 && Month<=12 && Day>=1 && Day<=DaysInMonth(Year, Month))
    return true;
  else
    return ErrorMsg;
}

function CheckDateString(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidDateString(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidTimeString(TimeString)
{
  TimeString = (''+TimeString).toLowerCase();
  if(TimeString=='')
    return 'Please specify time';
  var ErrorMsg = 'Invalid time ('+TimeString+'). Please use HH:MM[:SS][am|pm] format.';
  var AM = StringEndsWith(TimeString, 'am');
  var PM = StringEndsWith(TimeString, 'pm');
  if(AM || PM)
  {
    TimeString = TimeString.substr(0, TimeString.length-2);
    if(StringEndsWith(TimeString, ' '))
      TimeString = TimeString.substr(0, TimeString.length-1);
  }
  var HourMinSec = TimeString.split(':');
  while(HourMinSec.length<3)
    HourMinSec.push('0');
  if(!IsNumber(HourMinSec[0]) || !IsNumber(HourMinSec[1]) || !IsNumber(HourMinSec[2]))
    return ErrorMsg;
  HourMinSec[0] = parseInt(HourMinSec[0], 10);
  if(AM || PM)
    if(HourMinSec[0]<1 || HourMinSec[0]>12)
      return ErrorMsg;
  if(HourMinSec[0]>23 || parseInt(HourMinSec[1], 10)>59 || parseInt(HourMinSec[2], 10)>59)
    return ErrorMsg;
  return true;
}

function CheckTimeString(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidTimeString(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidIntegerString(IntegerString, DisplayLabel, MustBePositive)
{
  var ErrorMsg = 'Specify a valid numeric value for '+DisplayLabel+', please.';
  IntegerString = ''+IntegerString;
  if(IntegerString=='')
    return ErrorMsg;
  var StartIndex;
  if(!MustBePositive && IntegerString.substr(0, 1)=='-')
    StartIndex = 1;
  else
    StartIndex = 0;
  for(var i = StartIndex; i<IntegerString.length; i++)
    if(!IsNumber(IntegerString.substr(i, 1)))
      return ErrorMsg;
  return true;
}

function CheckIntegerString(InputID, DisplayLabel, MustBePositive)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidIntegerString(Input.val(), DisplayLabel, MustBePositive);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidFloatString(FloatString, DisplayLabel, MinValue, MaxValue)
{
  var ErrorMsg = 'Specify a valid floating-point value for '+DisplayLabel+', please.';
  FloatString = ''+FloatString;
  if(FloatString=='')
    return ErrorMsg;
  var Separator = null;
  var StartIndex;
  if(MinValue<0 && FloatString.substr(0, 1)=='-')
    StartIndex = 1;
  else
    StartIndex = 0;
  for(var i = StartIndex; i<FloatString.length; i++)
    if(FloatString.substr(i, 1)=='.' || FloatString.substr(i, 1)==',')
      if(Separator==null)
        Separator = i;
      else
        return ErrorMsg;
    else if(!IsDigit(FloatString.substr(i, 1)))
      return ErrorMsg;
  if(MinValue!=null && parseFloat(FloatString)<MinValue)
    return DisplayLabel+' must be not less than '+MinValue+'.';
  if(MaxValue!=null && parseFloat(FloatString)>MaxValue)
    return DisplayLabel+' must be not greater than '+MaxValue+'.';
  return true;
}

function CheckFloatString(InputID, DisplayLabel, MustBePositive)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidFloatString(Input.val(), DisplayLabel, MustBePositive);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidMoneyString(MoneyString, DisplayLabel, AllowNegatives)
{
  var ErrorMsg = 'Please specify a valid amount for '+DisplayLabel+' using NNN.NN format.';
  MoneyString = ''+MoneyString;
  if(MoneyString=='')
    return ErrorMsg;
  var StartIndex;
  if(AllowNegatives && MoneyString.substr(0, 1)=='-')
    StartIndex = 1;
  else
    StartIndex = 0;
  var Separator = null;
  for(var i = StartIndex; i<MoneyString.length; i++)
    if(MoneyString.substr(i, 1)=='.' || MoneyString.substr(i, 1)==',')
      if(Separator==null)
        Separator = i;
      else
        return ErrorMsg;
    else if(!IsDigit(MoneyString.substr(i, 1)))
      return ErrorMsg;
  if(Separator!=null && MoneyString.length-Separator>3)
    return 'Only two decimals after the point are allowed for '+DisplayLabel;
  return true;
}

function CheckMoneyString(InputID, DisplayLabel, AllowNegatives)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidMoneyString(Input.val(), DisplayLabel, AllowNegatives);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}


function ValidPhone(Phone, Strictly10Digits)
{
  var ErrorMsg = 'Please specify a valid phone number.';
  Phone = ''+Phone;
  if(Phone=='')
    return ErrorMsg;
  if(Phone.length>15)
    return ErrorMsg;
  var s = '';
  for(var i = 0; i<Phone.length; i++)
    if(IsDigit(Phone.substr(i, 1)))
      s += Phone.substr(i, 1);
  if(Strictly10Digits)
    MaxDigitCount = 10;
  else
    MaxDigitCount = 13;
  if(s.length<10 || s.length>MaxDigitCount || s.substr(0, 1)=='0')
    return ErrorMsg;
  return true;
}

function CheckPhone(InputID, Strictly10Digits)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidPhone(Input.val(), Strictly10Digits);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidAddress(Address, DisplayLabel)
{
  Address = ''+Address;
  if(Address=='')
    return 'Please specify '+DisplayLabel+'.';
  if(Address.length<5)
    return DisplayLabel+' should be at least 5 characters long.';
  if(!HasLetters(Address) || !HasDigits(Address))
    return DisplayLabel+' should contain both letters and digits.';
  var IndexOfSpace = Address.indexOf(' ');
  if(IndexOfSpace==-1 || IndexOfSpace==0 || IndexOfSpace==Address.length-1)
    return DisplayLabel+' should contain at least one space.';
  return true;
}

function CheckAddress(InputID, DisplayLabel)
{
  var Input = $('#'+InputID);
  if(!DisplayLabel)
    DisplayLabel = 'Address';
  var ErrorMsg = ValidAddress(Input.val(), DisplayLabel);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidPostalCode(PostalCode)
{
  if(ValidCanadianPostalCode(PostalCode)==true || ValidUSAPostalCode(PostalCode)==true)
    return true;
  return 'Invalid Zip/Postal Code';
}

function ValidCanadianPostalCode(CanadianPostalCode)
{
  CanadianPostalCode = ''+CanadianPostalCode;
  if(CanadianPostalCode=='')
    return 'Please specify a valid Canadian Postal Code.';
  CanadianPostalCode = CompressCanadianPostalCode(CanadianPostalCode);
  if(CanadianPostalCode.length!=6
        || !IsLetter(CanadianPostalCode.substr(0, 1))
        || !IsDigit(CanadianPostalCode.substr(1, 1))
        || !IsLetter(CanadianPostalCode.substr(2, 1))
        || !IsDigit(CanadianPostalCode.substr(3, 1))
        || !IsLetter(CanadianPostalCode.substr(4, 1))
        || !IsDigit(CanadianPostalCode.substr(5, 1)))
    return 'Please specify a valid Canadian Postal Code.';
  return true;
}

function CheckCanadianPostalCode(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidCanadianPostalCode(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidUSAPostalCode(USAPostalCode)
{
  USAPostalCode = ''+USAPostalCode;
  if(USAPostalCode=='')
    return 'Please specify a valid US Zip Code.';
  if(USAPostalCode.length!=5
        || !IsNumber(USAPostalCode))
    return 'Please specify a valid US Zip Code.';
  return true;
}

function CheckUSAPostalCode(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidUSAPostalCode(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function CheckPostalCode(InputID)
{
  var Input = $('#'+InputID);
  if(ValidCanadianPostalCode(Input.val())!=true && ValidUSAPostalCode(Input.val())!=true)
  {
    Input.focus();
    alert('Please specify a valid Postal Code.');
    return false;
  }
  return true;
}

function ValidCreditCardNumber(CreditCardNumber)
{
  CreditCardNumber = ''+CreditCardNumber;
  CreditCardNumber = CreditCardNumber.replace(/-/g, '');
  CreditCardNumber = CreditCardNumber.replace(/ /g, '');
  if(CreditCardNumber=='')
    return 'Please enter a Credit Card Number';
  var DigitCount = 0;
  for(var i = 0; i<CreditCardNumber.length; i++)
  {
    if(CreditCardNumber.substr(i, 1)==' ')
      continue;
    if(IsDigit(CreditCardNumber.substr(i, 1)))
      DigitCount++;
    else
      return '"'+CreditCardNumber+'" is not a valid credit card number - it should only contain'+
        ' digits';
  }
  if(DigitCount!=15 && DigitCount!=16)
    return '"'+CreditCardNumber+'" is not a valid credit card number - it should consist of 15 or'+
        ' 16 digits and optional spaces';
  return true;
}

function CheckCreditCardNumber(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidCreditCardNumber(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}


function ValidCreditCardCardholderName(Name, DisplayLabel)
{
  if(Name=='')
    return 'Please enter a '+DisplayLabel+'.';
  if(Name.length<3)
    return DisplayLabel+' is too short - it must be at least 3 characters long.';
  var IndexOfSpace = Name.indexOf(' ');
  if(IndexOfSpace==-1 || IndexOfSpace==0 || IndexOfSpace==Name.length-1)
    return DisplayLabel+' must contain a space between first name and last name.';
  return true;
}

function CheckCreditCardCardholderName(InputID, DisplayLabel)
{
  var Input = $('#'+InputID);
  if(!DisplayLabel)
    DisplayLabel = 'Cardholder Name';
  var ErrorMsg = ValidCreditCardCardholderName(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidCVV2(CVV2)
{
  CVV2 = ''+CVV2;
  if(CVV2=='')
    return 'Please enter CVV2 security code for your credit card';
  else
  {
    var l = CVV2.length;
    if(l!=3 && l!=4)
      return '"'+CVV2+'" is not a valid credit card CVV2.';
    else
      for(var i = 0; i<l; i++)
        if(!IsDigit(CVV2.substr(i, 1)))
        {
          return '"'+CVV2+'" is not a valid credit card CVV2 - it should only contain numbers.';
          break;
        }
  }
  return true;
}

function CheckCVV2(InputID)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidCVV2(Input.val());
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidSlug(Slug, FieldDisplayLabel, IsMandatory)
{
  Slug = ''+Slug;
  if(Slug=='')
    return 'Please specify a value for '+FieldDisplayLabel;
  if(!IsAlphaNumeric(Slug, '-_'))
    return 'Slug must contain only English letters, digits, underscores (_) and dashes (-).';
  return true;
}

function CheckSlug(InputID, FieldDisplayLabel, IsMandatory)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidSlug(Input.val(), FieldDisplayLabel, IsMandatory);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidPassword(Password, MinPasswordLength, MaxPasswordLength, MinSecurityScore)
{
  Password = ''+Password;
  if(Password=='')
    return 'Please specify a Password.';
  else if(Password.length<MinPasswordLength)
    return 'Minimum length for a Password is '+MinPasswordLength+' characters.';
  else if(Password.length>MaxPasswordLength)
    return 'Maximum length for a Password is '+MaxPasswordLength+' characters.';
  else if(MinSecurityScore>0 && PasswordSecurityScore(Password)<MinSecurityScore)
    return 'Please enter a more secure Password. Please use a combination of digits, upper case'+
              ' letters, lower case letters and special characters to make your password more secure.';
  else if(Password.indexOf(' ')>-1)
    return 'Spaces are not allowed in the password.';
  return true;
}

function CheckPassword(InputID, MinPasswordLength, MaxPasswordLength, MinSecurityScore)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidPassword(Input.val(), MinPasswordLength, MaxPasswordLength, MinSecurityScore);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}

function ValidColor(Str, DisplayLabel)
{
  Str = ''+Str;
  if(Str=='')
    return 'Specify '+DisplayLabel+', please.';
  else if(Str.length!=6)
    return 'Color value must be 6 characters long.';
  else
  {
    var HexDigits = '0123456789ABCDEF';
    Str = Str.toUpperCase();
    for(var i = 0; i<6; i++)
      if(HexDigits.indexOf(Str[i])==-1)
        return 'Color value must be in hexadecimal format, for example: "ABCDEF".';
  }
  return true;
}

function CheckColor(InputID, DisplayLabel)
{
  var Input = $('#'+InputID);
  var ErrorMsg = ValidColor(Input.val(), DisplayLabel);
  if(ErrorMsg!=true)
  {
    Input.focus();
    alert(ErrorMsg);
    return false;
  }
  return true;
}
