/*
   Copyright 2010 Aaron J. Radke

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package cc.drx

object Glob{
   /**smart vim/magic like constructor to not ignore the case if an upper case character exists in the pattern*/
   def apply(glob:String):Glob = new Glob(glob, !glob.exists{c => c >= 'A' && c <= 'Z'})
   def ignoreCase(glob:String):Glob = new Glob(glob, true)
   def matchCase(glob:String):Glob = new Glob(glob, false)
   //def apply(glob:String,ignoreCase:Boolean):Glob = new Glob(glob,ignoreCase)
   implicit object ParsableGlob extends Parsable[Glob]{  def apply(glob:String):Glob = Glob(glob)  }

   final class GlobStringContext(val sc:StringContext) extends AnyVal{
     def glob(args:Any*):Glob = Glob(sc.s(args:_*))
   }

}
/**Glob version of a regex where * zero or more char and ? is any single character
 * see https://en.wikipedia.org/wiki/Glob_(programming)
 */
class Glob(val glob: String, useIgnoreCase:Boolean, starPat:String="""[^\\/]"""){
   override def toString = "Glob." + (if(useIgnoreCase) "ignoreCase" else "matchCase") + "(" + glob + ")"

   def ignoreCase:Glob = new Glob(glob, true)
   def matchCase:Glob = new Glob(glob, false)

   //--List of characters that need to be escaped in regex https://stackoverflow.com/a/26228852/622016
   lazy val regex:scala.util.matching.Regex = {
     val pat = glob
                 .trim
                 .replace("\\*","@LiteralStar")
                 .replace("\\?","@LiteralQuestion")
                 .replace("\\\\","@LiteralBackslash")
                 .replace("**","(Invalid)")
                 .replace("*","@SingleStar")
                 .replace("\\","\\\\")  //literal slash
                 .replace(".","\\.") //literal dot
                 .replace("-","\\-") //stop the - character
                 .replace("?","(.?)")  //zero or one
                 .replace("@LiteralStar","\\*")
                 .replace("@LiteralQuestion","\\?")
                 .replace("@LiteralBackslash","\\")
                 .replace("@SingleStar",s"($starPat*)") //match zero or more equivocally
                 .replace("(Invalid)","(.*)") //match zero or more (unequivocally)
     val flags = if(useIgnoreCase) "(?i)" else ""
     ("^" + flags + pat + "$").r   //use ^ and $ to anchor the match to the full string
   }

   def matches(str:String):Boolean = find(str).isDefined
   def doesNotMatch(str:String):Boolean = !matches(str)
   def find(str:String) = regex findFirstMatchIn str
   def subgroups(str:String):Option[List[String]] = regex.findFirstMatchIn(str).map{_.subgroups}
   //retrieve subgroups of a match
   def unapplySeq(str:String):Option[List[String]] = subgroups(str)
   // def unapply(str:String):Option[String] = if(matches(str)) Some(str) else None //conflicts with unnaplySeq
}