/*private void tryCustomFloats(string[] floats) {
         *  float total = 0f;
         *  foreach (string f in floats) {
         *      pos = 0;
         *      bvhText = f;
         *      float v;
         *      getFloat(out v);
         *      total += v;
         *  }
         *  Debug.Log("Custom: " + total);
         * }
         *
         * private void tryStandardFloats(string[] floats) {
         *  IFormatProvider fp = CultureInfo.InvariantCulture;
         *  float total = 0f;
         *  foreach (string f in floats) {
         *      float v = float.Parse(f, fp);
         *      total += v;
         *  }
         *  Debug.Log("Standard: " + total);
         * }
         *
         * private void tryCustomInts(string[] ints) {
         *  int total = 0;
         *  foreach (string i in ints) {
         *      pos = 0;
         *      bvhText = i;
         *      int v;
         *      getInt(out v);
         *      total += v;
         *  }
         *  Debug.Log("Custom: " + total);
         * }
         *
         * private void tryStandardInts(string[] ints) {
         *  IFormatProvider fp = CultureInfo.InvariantCulture;
         *  int total = 0;
         *  foreach (string i in ints) {
         *      int v = int.Parse(i, fp);
         *      total += v;
         *  }
         *  Debug.Log("Standard: " + total);
         * }
         *
         * public void benchmark () {
         *  string[] floats = new string[105018];
         *  string[] ints = new string[105018];
         *  for (int i = 0; i < floats.Length; i++) {
         *      floats[i] = UnityEngine.Random.Range(-180f, 180f).ToString();
         *  }
         *  for (int i = 0; i < ints.Length; i++) {
         *      ints[i] = ((int)Mathf.Round(UnityEngine.Random.Range(-180f, 18000f))).ToString();
         *  }
         *  tryCustomFloats(floats);
         *  tryStandardFloats(floats);
         *  tryCustomInts(ints);
         *  tryStandardInts(ints);
         * }*/

        private void parse(bool overrideFrameTime, float time)
        {
            // Prepare character table
            if (charMap == null)
            {
                charMap = new char[256];
                for (int i = 0; i < 256; i++)
                {
                    if (i >= 'a' && i <= 'z')
                    {
                        charMap[i] = (char)(i - 'a' + 'A');
                    }
                    else if (i == '\t' || i == '\n' || i == '\r')
                    {
                        charMap[i] = ' ';
                    }
                    else
                    {
                        charMap[i] = (char)i;
                    }
                }
            }

            // Parse skeleton
            skip();
            assureExpect("HIERARCHY");

            boneList = new List <BVHBone>();
            root     = new BVHBone(this, true);

            // Parse meta data
            skip();
            assureExpect("MOTION");
            skip();
            assureExpect("FRAMES:");
            skip();
            assure("frame number", getInt(out frames));
            skip();
            assureExpect("FRAME TIME:");
            skip();
            assure("frame time", getFloat(out frameTime));

            if (overrideFrameTime)
            {
                frameTime = time;
            }

            // Prepare channels
            int totalChannels = 0;

            foreach (BVHBone bone in boneList)
            {
                totalChannels += bone.channelNumber;
            }
            int channel = 0;

            channels = new float[totalChannels][];
            foreach (BVHBone bone in boneList)
            {
                for (int i = 0; i < bone.channelNumber; i++)
                {
                    channels[channel] = new float[frames];
                    bone.channels[bone.channelOrder[i]].values = channels[channel++];
                }
            }

            // Parse frames
            for (int i = 0; i < frames; i++)
            {
                newline();
                for (channel = 0; channel < totalChannels; channel++)
                {
                    skipInLine();
                    assure("channel value", getFloat(out channels[channel][i]));
                }
            }
        }
            public BVHBone(BVHParser parser, bool rootBone)
            {
                bp = parser;
                bp.boneList.Add(this);
                channels     = new BVHChannel[6];
                channelOrder = new int[6] {
                    0, 1, 2, 5, 3, 4
                };
                children = new List <BVHBone>();

                bp.skip();
                if (rootBone)
                {
                    bp.assureExpect("ROOT");
                }
                else
                {
                    bp.assureExpect("JOINT");
                }
                bp.assure("joint name", bp.getString(out name));
                bp.skip();
                bp.assureExpect("{");
                bp.skip();
                bp.assureExpect("OFFSET");
                bp.skip();
                bp.assure("offset X", bp.getFloat(out offsetX));
                bp.skip();
                bp.assure("offset Y", bp.getFloat(out offsetY));
                bp.skip();
                bp.assure("offset Z", bp.getFloat(out offsetZ));
                bp.skip();
                bp.assureExpect("CHANNELS");

                bp.skip();
                bp.assure("channel number", bp.getInt(out channelNumber));
                bp.assure("valid channel number", channelNumber >= 1 && channelNumber <= 6);

                for (int i = 0; i < channelNumber; i++)
                {
                    bp.skip();
                    int channelId;
                    bp.assure("channel ID", bp.getChannel(out channelId));
                    channelOrder[i]             = channelId;
                    channels[channelId].enabled = true;
                }

                char peek = ' ';

                do
                {
                    float ignored;
                    bp.skip();
                    bp.assure("child joint", bp.peek(out peek));
                    switch (peek)
                    {
                    case 'J':
                        BVHBone child = new BVHBone(bp, false);
                        children.Add(child);
                        break;

                    case 'E':
                        bp.assureExpect("End Site");
                        bp.skip();
                        bp.assureExpect("{");
                        bp.skip();
                        bp.assureExpect("OFFSET");
                        bp.skip();
                        bp.assure("end site offset X", bp.getFloat(out ignored));
                        bp.skip();
                        bp.assure("end site offset Y", bp.getFloat(out ignored));
                        bp.skip();
                        bp.assure("end site offset Z", bp.getFloat(out ignored));
                        bp.skip();
                        bp.assureExpect("}");
                        break;

                    case '}':
                        bp.assureExpect("}");
                        break;

                    default:
                        bp.assure("child joint", false);
                        break;
                    }
                } while (peek != '}');
            }